fonc: travail en cours sur scripts page Produit
This commit is contained in:
parent
08ad871e0c
commit
e01cd343bc
30 changed files with 1110 additions and 994 deletions
|
|
@ -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
|
||||
*/
|
||||
function load_page_resources(): void
|
||||
{
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-a-propos',
|
||||
path: '/assets/css/pages/page-modele-simple.css',
|
||||
);
|
||||
function load_page_resources(): void {
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-a-propos',
|
||||
path: '/assets/css/pages/page-modele-simple.css',
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', load_page_resources(...));
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ $context = Timber::context();
|
|||
$templates = ['accueil.twig'];
|
||||
|
||||
add_action('wp_enqueue_scripts', function (): void {
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-accueil',
|
||||
path: '/assets/css/pages/page-accueil.css',
|
||||
);
|
||||
Resource::enqueue_script_module_file(
|
||||
id: 'haiku-atelier-2024-scripts-page-accueil',
|
||||
path: '/assets/js/scripts-page-accueil.js',
|
||||
);
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-accueil',
|
||||
path: '/assets/css/pages/page-accueil.css',
|
||||
);
|
||||
Resource::enqueue_script_module_file(
|
||||
id: 'haiku-atelier-2024-scripts-page-accueil',
|
||||
path: '/assets/js/scripts-page-accueil.js',
|
||||
);
|
||||
});
|
||||
|
||||
Timber::render(data: $context, filenames: $templates);
|
||||
|
|
|
|||
|
|
@ -24,20 +24,20 @@ $templates = ['a-propos.twig'];
|
|||
$image_dimensions = getimagesize(filename: get_template_directory() . '/assets/img/about/haikuabout.png');
|
||||
|
||||
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;
|
||||
|
||||
add_action('wp_enqueue_scripts', function (): void {
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-a-propos',
|
||||
path: '/assets/css/pages/page-a-propos.css',
|
||||
);
|
||||
Resource::enqueue_script_module_file(
|
||||
id: 'haiku-atelier-2024-scripts-page-a-propos',
|
||||
path: '/assets/js/scripts-page-a-propos.js',
|
||||
);
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-a-propos',
|
||||
path: '/assets/css/pages/page-a-propos.css',
|
||||
);
|
||||
Resource::enqueue_script_module_file(
|
||||
id: 'haiku-atelier-2024-scripts-page-a-propos',
|
||||
path: '/assets/js/scripts-page-a-propos.js',
|
||||
);
|
||||
});
|
||||
|
||||
Timber::render(data: $context, filenames: $templates);
|
||||
|
|
|
|||
|
|
@ -22,22 +22,20 @@ use WC_Session_Handler;
|
|||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// TODO: Appliquer le bon calcul pour les montants vs. percentages
|
||||
function get_discount_amount(WC_Coupon $coupon)
|
||||
{
|
||||
if ($coupon->get_discount_type() === 'fixed_cart') {
|
||||
return $coupon->get_amount() * 100;
|
||||
} else {
|
||||
return $coupon->get_amount();
|
||||
}
|
||||
function get_discount_amount(WC_Coupon $coupon) {
|
||||
if ($coupon->get_discount_type() === 'fixed_cart') {
|
||||
return $coupon->get_amount() * 100;
|
||||
} else {
|
||||
return $coupon->get_amount();
|
||||
}
|
||||
}
|
||||
|
||||
function get_discount_duration(WC_Coupon $coupon): string
|
||||
{
|
||||
if ($coupon->get_discount_type() === 'fixed_cart') {
|
||||
return 'once';
|
||||
} else {
|
||||
return 'forever';
|
||||
}
|
||||
function get_discount_duration(WC_Coupon $coupon): string {
|
||||
if ($coupon->get_discount_type() === 'fixed_cart') {
|
||||
return 'once';
|
||||
} else {
|
||||
return 'forever';
|
||||
}
|
||||
}
|
||||
|
||||
// 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. */
|
||||
$urls = [
|
||||
'accueil' => get_page_link(get_page_by_path('home')),
|
||||
'succes_commande' => get_page_link(get_page_by_path('successful-order')),
|
||||
'echec_commande' => get_page_link(get_page_by_path('failed-order')),
|
||||
'accueil' => get_page_link(get_page_by_path('home')),
|
||||
'succes_commande' => get_page_link(get_page_by_path('successful-order')),
|
||||
'echec_commande' => get_page_link(get_page_by_path('failed-order')),
|
||||
];
|
||||
|
||||
// Redirige à la page d'accueil si le Panier est vide
|
||||
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
|
||||
/** @var string $order_id */
|
||||
$order_id = $_GET['order_id'];
|
||||
if (!$order_id) {
|
||||
$reponse = ['succes' => false, 'status' => 'order_key is missing'];
|
||||
echo json_encode($reponse);
|
||||
http_response_code(400);
|
||||
$reponse = ['succes' => false, 'status' => 'order_key is missing'];
|
||||
echo json_encode($reponse);
|
||||
http_response_code(400);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var string $order_key */
|
||||
$order_key = $_GET['order_key'];
|
||||
if (!$order_key) {
|
||||
$reponse = ['succes' => false, 'status' => 'order_key is missing'];
|
||||
echo json_encode($reponse);
|
||||
http_response_code(400);
|
||||
$reponse = ['succes' => false, 'status' => 'order_key is missing'];
|
||||
echo json_encode($reponse);
|
||||
http_response_code(400);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 */
|
||||
$articles = collect($panier->get_cart())
|
||||
->map(static function ($article_panier) {
|
||||
$titre_produit = match ('variable' === $article_panier['data']?->get_type()) {
|
||||
true => $article_panier['data']?->get_title()
|
||||
. ' ('
|
||||
. explode(': ', (string) $article_panier['data']?->get_attribute_summary())[1]
|
||||
. ')',
|
||||
false => $article_panier['data']?->get_title(),
|
||||
};
|
||||
->map(static function ($article_panier) {
|
||||
$titre_produit = match ('variable' === $article_panier['data']?->get_type()) {
|
||||
true => $article_panier['data']?->get_title()
|
||||
. ' ('
|
||||
. explode(': ', (string) $article_panier['data']?->get_attribute_summary())[1]
|
||||
. ')',
|
||||
false => $article_panier['data']?->get_title(),
|
||||
};
|
||||
|
||||
return [
|
||||
'price_data' => [
|
||||
'currency' => 'EUR',
|
||||
'product_data' => [
|
||||
'name' => $titre_produit,
|
||||
'images' => [wp_get_attachment_image_url($article_panier['data']?->get_image_id())],
|
||||
],
|
||||
'unit_amount' => $article_panier['data']?->get_price() * 100,
|
||||
],
|
||||
'quantity' => $article_panier['quantity'],
|
||||
];
|
||||
})
|
||||
->values()
|
||||
->toArray();
|
||||
return [
|
||||
'price_data' => [
|
||||
'currency' => 'EUR',
|
||||
'product_data' => [
|
||||
'name' => $titre_produit,
|
||||
'images' => [wp_get_attachment_image_url($article_panier['data']?->get_image_id())],
|
||||
],
|
||||
'unit_amount' => $article_panier['data']?->get_price() * 100,
|
||||
],
|
||||
'quantity' => $article_panier['quantity'],
|
||||
];
|
||||
})
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
// Récupère la Commande et la Méthode de Livraison
|
||||
/** @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.
|
||||
if (empty($methode_livraison['nom'])) {
|
||||
$methode_livraison['nom'] = 'Free';
|
||||
$methode_livraison['nom'] = 'Free';
|
||||
}
|
||||
|
||||
// Sélectionne la clé API Stripe
|
||||
|
|
@ -129,39 +127,39 @@ Stripe::setApiKey(Config::get('STRIPE_API_SECRET'));
|
|||
// Met à jour les Codes promos
|
||||
$coupons_stripe = collect(Coupon::all()->data);
|
||||
$coupons_wc = collect(WC()->cart->get_coupons())
|
||||
->map(static fn(WC_Coupon $coupon): array => [
|
||||
'currency' => 'EUR',
|
||||
'duration' => get_discount_duration($coupon),
|
||||
'fixed_cart' === $coupon->get_discount_type() ? 'amount_off' : 'percent_off' => get_discount_amount($coupon),
|
||||
'id' => $coupon->get_code(),
|
||||
'name' => $coupon->get_code(),
|
||||
])
|
||||
->each(static function (array $item) use ($coupons_stripe): void {
|
||||
// Si le code promo n'existe pas, le créer
|
||||
if (!$coupons_stripe->contains('name', $item['name'])) {
|
||||
Coupon::create($item);
|
||||
}
|
||||
});
|
||||
->map(static fn(WC_Coupon $coupon): array => [
|
||||
'currency' => 'EUR',
|
||||
'duration' => get_discount_duration($coupon),
|
||||
'fixed_cart' === $coupon->get_discount_type() ? 'amount_off' : 'percent_off' => get_discount_amount($coupon),
|
||||
'id' => $coupon->get_code(),
|
||||
'name' => $coupon->get_code(),
|
||||
])
|
||||
->each(static function (array $item) use ($coupons_stripe): void {
|
||||
// Si le code promo n'existe pas, le créer
|
||||
if (!$coupons_stripe->contains('name', $item['name'])) {
|
||||
Coupon::create($item);
|
||||
}
|
||||
});
|
||||
$reductions_stripe = $coupons_wc
|
||||
->map(static fn($coupon): array => ['coupon' => $coupon['name']])
|
||||
->values()
|
||||
->toArray();
|
||||
->map(static fn($coupon): array => ['coupon' => $coupon['name']])
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
/** @var Session $session_checkout_stripe */
|
||||
$session_checkout_stripe = Session::create([
|
||||
'cancel_url' => $urls['echec_commande'],
|
||||
'customer_email' => $email_client,
|
||||
'discounts' => $reductions_stripe,
|
||||
'line_items' => $articles,
|
||||
'mode' => 'payment',
|
||||
'success_url' => $urls['succes_commande'] . '?session_id={CHECKOUT_SESSION_ID}',
|
||||
'metadata' => ['order_id' => $order_id, 'order_key' => $order_key],
|
||||
'shipping_options' => [['shipping_rate_data' => [
|
||||
'display_name' => $methode_livraison['nom'],
|
||||
'fixed_amount' => ['amount' => $methode_livraison['cout'], 'currency' => 'EUR'],
|
||||
'tax_behavior' => 'inclusive',
|
||||
'type' => 'fixed_amount',
|
||||
]]],
|
||||
'cancel_url' => $urls['echec_commande'],
|
||||
'customer_email' => $email_client,
|
||||
'discounts' => $reductions_stripe,
|
||||
'line_items' => $articles,
|
||||
'mode' => 'payment',
|
||||
'success_url' => $urls['succes_commande'] . '?session_id={CHECKOUT_SESSION_ID}',
|
||||
'metadata' => ['order_id' => $order_id, 'order_key' => $order_key],
|
||||
'shipping_options' => [['shipping_rate_data' => [
|
||||
'display_name' => $methode_livraison['nom'],
|
||||
'fixed_amount' => ['amount' => $methode_livraison['cout'], 'currency' => 'EUR'],
|
||||
'tax_behavior' => 'inclusive',
|
||||
'type' => 'fixed_amount',
|
||||
]]],
|
||||
], ['idempotency_key' => Uuid::v4()]);
|
||||
// echo json_encode($session_checkout_stripe);
|
||||
header('HTTP/1.1 303 See Other');
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ $context = Timber::context();
|
|||
$templates = ['echec-commande.twig'];
|
||||
|
||||
add_action('wp_enqueue_scripts', function (): void {
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-modele-simple',
|
||||
path: '/assets/css/pages/page-modele-simple.css',
|
||||
);
|
||||
Resource::enqueue_style_file(
|
||||
handle: 'haiku-atelier-2024-styles-page-modele-simple',
|
||||
path: '/assets/css/pages/page-modele-simple.css',
|
||||
);
|
||||
});
|
||||
|
||||
// Rendu
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ $context = Timber::context();
|
|||
$templates = ['cgv.twig'];
|
||||
|
||||
add_action('wp_enqueue_scripts', function (): void {
|
||||
Resource::enqueue_style_file(
|
||||
handle: '/assets/css/pages/page-modele-simple.css',
|
||||
path: '/assets/css/pages/page-modele-simple.css',
|
||||
);
|
||||
Resource::enqueue_style_file(
|
||||
handle: '/assets/css/pages/page-modele-simple.css',
|
||||
path: '/assets/css/pages/page-modele-simple.css',
|
||||
);
|
||||
});
|
||||
|
||||
// Rendu
|
||||
|
|
|
|||
|
|
@ -9,39 +9,36 @@ declare(strict_types=1);
|
|||
use Carbon_Fields\Container;
|
||||
use Carbon_Fields\Field;
|
||||
|
||||
function cree_champs_personnalises_produit(): void
|
||||
{
|
||||
Container::make('post_meta', "Product's Details")
|
||||
->where('post_type', '=', 'product')
|
||||
->add_fields([
|
||||
// Galerie des photos Produit
|
||||
Field::make('media_gallery', 'photos_colonne_gauche', __('Left Column Photos'))
|
||||
->set_type(['image'])
|
||||
->set_duplicates_allowed(false),
|
||||
// Galerie des photos portées
|
||||
Field::make('media_gallery', 'photos_colonne_droite', __('Right Column Photos'))
|
||||
->set_type(['image'])
|
||||
->set_duplicates_allowed(false),
|
||||
// Texte des détails du Produit
|
||||
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 cree_champs_personnalises_produit(): void {
|
||||
Container::make('post_meta', "Product's Details")
|
||||
->where('post_type', '=', 'product')
|
||||
->add_fields([
|
||||
// Galerie des photos Produit
|
||||
Field::make('media_gallery', 'photos_colonne_gauche', __('Left Column Photos'))
|
||||
->set_type(['image'])
|
||||
->set_duplicates_allowed(false),
|
||||
// Galerie des photos portées
|
||||
Field::make('media_gallery', 'photos_colonne_droite', __('Right Column Photos'))
|
||||
->set_type(['image'])
|
||||
->set_duplicates_allowed(false),
|
||||
// Texte des détails du Produit
|
||||
Field::make('rich_text', 'haiku_details_produit', __("Product's Details")),
|
||||
]);
|
||||
}
|
||||
|
||||
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();
|
||||
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 {
|
||||
$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');
|
||||
|
|
|
|||
|
|
@ -8,74 +8,69 @@
|
|||
|
||||
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.
|
||||
*
|
||||
* @author Anthony Hortin <http://maddisondesigns.com>
|
||||
* @license http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* @see https://github.com/maddisondesigns
|
||||
* Enqueue our scripts and styles.
|
||||
*/
|
||||
final class ControlesPersonnalises extends WP_Customize_Control
|
||||
{
|
||||
/** The type of control being rendered. */
|
||||
public $type = 'editeur_tinymce';
|
||||
public function enqueue(): 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue our scripts and styles.
|
||||
*/
|
||||
public function enqueue(): 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
|
||||
{ ?>
|
||||
/**
|
||||
* Render the control in the customizer.
|
||||
*/
|
||||
public function render_content(): void { ?>
|
||||
<div class="tinymce-control">
|
||||
<span class="customize-control-title"><?php echo esc_html($this->label); ?></span>
|
||||
<?php if (!empty($this->description)) { ?>
|
||||
<span class="customize-control-description"><?php echo esc_html($this->description); ?></span>
|
||||
<?php } ?>
|
||||
<textarea id="<?php echo
|
||||
esc_attr($this->id)
|
||||
esc_attr($this->id)
|
||||
; ?>" class="customize-control-tinymce-editor" <?php $this->link(); ?>><?php echo
|
||||
esc_html($this->value())
|
||||
esc_html($this->value())
|
||||
; ?></textarea>
|
||||
</div>
|
||||
<?php }
|
||||
|
||||
/**
|
||||
* Pass our TinyMCE toolbar string to JavaScript.
|
||||
*/
|
||||
public function to_json(): void
|
||||
{
|
||||
parent::to_json();
|
||||
/**
|
||||
* Pass our TinyMCE toolbar string to JavaScript.
|
||||
*/
|
||||
public function to_json(): void {
|
||||
parent::to_json();
|
||||
|
||||
$this->json['skyrockettinymcetoolbar1'] = isset($this->input_attrs['toolbar1'])
|
||||
? esc_attr($this->input_attrs['toolbar1'])
|
||||
: 'bold italic bullist numlist alignleft aligncenter alignright link';
|
||||
$this->json['skyrockettinymcetoolbar1'] = isset($this->input_attrs['toolbar1'])
|
||||
? esc_attr($this->input_attrs['toolbar1'])
|
||||
: 'bold italic bullist numlist alignleft aligncenter alignright link';
|
||||
|
||||
$this->json['skyrockettinymcetoolbar2'] = isset($this->input_attrs['toolbar2'])
|
||||
? esc_attr($this->input_attrs['toolbar2'])
|
||||
: '';
|
||||
$this->json['skyrocketmediabuttons'] = isset($this->input_attrs['mediaButtons'])
|
||||
&& $this->input_attrs['mediaButtons'] === true
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
$this->json['skyrockettinymcetoolbar2'] = isset($this->input_attrs['toolbar2'])
|
||||
? esc_attr($this->input_attrs['toolbar2'])
|
||||
: '';
|
||||
$this->json['skyrocketmediabuttons'] = isset($this->input_attrs['mediaButtons'])
|
||||
&& $this->input_attrs['mediaButtons'] === true
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_action('customize_register', 'enregistre_controle_personnalise_tinymce');
|
||||
|
|
|
|||
|
|
@ -11,116 +11,113 @@ use function is_float;
|
|||
use function is_int;
|
||||
use function is_string;
|
||||
|
||||
final readonly class Cart
|
||||
{
|
||||
public function __construct() {}
|
||||
final readonly class Cart {
|
||||
public function __construct() {}
|
||||
|
||||
/** La valeur par défaut d'une donnée invalide du Panier. */
|
||||
private const string DEFAULT_VALUE = '0.00';
|
||||
/** La valeur par défaut d'une donnée invalide du Panier. */
|
||||
private const string DEFAULT_VALUE = '0.00';
|
||||
|
||||
/**
|
||||
* Retourne la liste des pays acceptés pour la livraison.
|
||||
*
|
||||
* @return array<int,string>
|
||||
*/
|
||||
public static function get_allowed_countries(): array
|
||||
{
|
||||
return [
|
||||
'AD',
|
||||
'AL',
|
||||
'AM',
|
||||
'AR',
|
||||
'AT',
|
||||
'AU',
|
||||
'BA',
|
||||
'BE',
|
||||
'BG',
|
||||
'BR',
|
||||
'CA',
|
||||
'CH',
|
||||
'CL',
|
||||
'CR',
|
||||
'CU',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DE',
|
||||
'DK',
|
||||
'DZ',
|
||||
'EE',
|
||||
'EG',
|
||||
'ES',
|
||||
'FI',
|
||||
'FR',
|
||||
'GF',
|
||||
'GP',
|
||||
'GR',
|
||||
'HR',
|
||||
'HU',
|
||||
'IE',
|
||||
'IS',
|
||||
'IT',
|
||||
'JP',
|
||||
'KR',
|
||||
'LB',
|
||||
'LI',
|
||||
'LT',
|
||||
'LU',
|
||||
'LV',
|
||||
'MA',
|
||||
'MD',
|
||||
'ME',
|
||||
'MF',
|
||||
'MQ',
|
||||
'MT',
|
||||
'MX',
|
||||
'NC',
|
||||
'NL',
|
||||
'NO',
|
||||
'NZ',
|
||||
'PF',
|
||||
'PL',
|
||||
'PM',
|
||||
'PS',
|
||||
'PT',
|
||||
'RE',
|
||||
'RO',
|
||||
'SE',
|
||||
'SI',
|
||||
'SK',
|
||||
'SM',
|
||||
'TN',
|
||||
'TR',
|
||||
'TW',
|
||||
'US',
|
||||
'YT',
|
||||
'ZA',
|
||||
];
|
||||
/**
|
||||
* Retourne la liste des pays acceptés pour la livraison.
|
||||
*
|
||||
* @return array<int,string>
|
||||
*/
|
||||
public static function get_allowed_countries(): array {
|
||||
return [
|
||||
'AD',
|
||||
'AL',
|
||||
'AM',
|
||||
'AR',
|
||||
'AT',
|
||||
'AU',
|
||||
'BA',
|
||||
'BE',
|
||||
'BG',
|
||||
'BR',
|
||||
'CA',
|
||||
'CH',
|
||||
'CL',
|
||||
'CR',
|
||||
'CU',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DE',
|
||||
'DK',
|
||||
'DZ',
|
||||
'EE',
|
||||
'EG',
|
||||
'ES',
|
||||
'FI',
|
||||
'FR',
|
||||
'GF',
|
||||
'GP',
|
||||
'GR',
|
||||
'HR',
|
||||
'HU',
|
||||
'IE',
|
||||
'IS',
|
||||
'IT',
|
||||
'JP',
|
||||
'KR',
|
||||
'LB',
|
||||
'LI',
|
||||
'LT',
|
||||
'LU',
|
||||
'LV',
|
||||
'MA',
|
||||
'MD',
|
||||
'ME',
|
||||
'MF',
|
||||
'MQ',
|
||||
'MT',
|
||||
'MX',
|
||||
'NC',
|
||||
'NL',
|
||||
'NO',
|
||||
'NZ',
|
||||
'PF',
|
||||
'PL',
|
||||
'PM',
|
||||
'PS',
|
||||
'PT',
|
||||
'RE',
|
||||
'RO',
|
||||
'SE',
|
||||
'SI',
|
||||
'SK',
|
||||
'SM',
|
||||
'TN',
|
||||
'TR',
|
||||
'TW',
|
||||
'US',
|
||||
'YT',
|
||||
'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_int($cart_value) || is_float($cart_value)) {
|
||||
return self::format_number($cart_value);
|
||||
}
|
||||
if (is_string($cart_value)) {
|
||||
$number = Number::parseInt($cart_value);
|
||||
$number = is_bool($number) ? 0 : $number;
|
||||
|
||||
if (is_string($cart_value)) {
|
||||
$number = Number::parseInt($cart_value);
|
||||
$number = is_bool($number) ? 0 : $number;
|
||||
|
||||
return self::format_number($number);
|
||||
}
|
||||
|
||||
return '0.00';
|
||||
return self::format_number($number);
|
||||
}
|
||||
|
||||
private static function format_number(int|float $number): string
|
||||
{
|
||||
$formatted_number = Number::format(
|
||||
number: $number,
|
||||
// precision et max_precision sont mutuellement exclusifs.
|
||||
precision: 2,
|
||||
locale: 'fr',
|
||||
);
|
||||
return is_bool($formatted_number) ? self::DEFAULT_VALUE : $formatted_number;
|
||||
}
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
private static function format_number(int|float $number): string {
|
||||
$formatted_number = Number::format(
|
||||
number: $number,
|
||||
// precision et max_precision sont mutuellement exclusifs.
|
||||
precision: 2,
|
||||
locale: 'fr',
|
||||
);
|
||||
|
||||
return is_bool($formatted_number) ? self::DEFAULT_VALUE : $formatted_number;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,34 +6,32 @@ namespace HaikuAtelier\Data;
|
|||
|
||||
use WC_Product;
|
||||
|
||||
final readonly class ProductVariation
|
||||
{
|
||||
/**
|
||||
* @param int $id L'ID de la Variation
|
||||
* @param string $price Le prix de la Variation
|
||||
* @param list<ProductVariationAttribute> $attributes Les attributs appliqués à la Variation
|
||||
*/
|
||||
private function __construct(
|
||||
public int $id,
|
||||
public string $price,
|
||||
public array $attributes,
|
||||
) {}
|
||||
final readonly class ProductVariation {
|
||||
/**
|
||||
* @param int $id L'ID de la Variation
|
||||
* @param string $price Le prix de la Variation
|
||||
* @param list<ProductVariationAttribute> $attributes Les attributs appliqués à la Variation
|
||||
*/
|
||||
private function __construct(
|
||||
public int $id,
|
||||
public string $price,
|
||||
public array $attributes,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Créé une nouvelle instance de `ProductVariation` à partir d'un `WC_Product`.
|
||||
*/
|
||||
public static function new(WC_Product $product): self
|
||||
{
|
||||
$id = $product->get_id();
|
||||
$price = $product->get_price();
|
||||
/** @var list<ProductVariationAttribute> */
|
||||
$attributes = array_map(
|
||||
/** @phpstan-ignore argument.type (Impossible à satisfaire) */
|
||||
static fn(string $key, string $value) => new ProductVariationAttribute($key, $value),
|
||||
array_keys($product->get_attributes()),
|
||||
array_values($product->get_attributes()),
|
||||
);
|
||||
/**
|
||||
* Créé une nouvelle instance de `ProductVariation` à partir d'un `WC_Product`.
|
||||
*/
|
||||
public static function new(WC_Product $product): self {
|
||||
$id = $product->get_id();
|
||||
$price = $product->get_price();
|
||||
/** @var list<ProductVariationAttribute> */
|
||||
$attributes = array_map(
|
||||
/** @phpstan-ignore argument.type (Impossible à satisfaire) */
|
||||
static fn(string $key, string $value) => new ProductVariationAttribute($key, $value),
|
||||
array_keys($product->get_attributes()),
|
||||
array_values($product->get_attributes()),
|
||||
);
|
||||
|
||||
return new self($id, $price, $attributes);
|
||||
}
|
||||
return new self($id, $price, $attributes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace HaikuAtelier\Data;
|
||||
|
||||
final readonly class ProductVariationAttribute
|
||||
{
|
||||
/**
|
||||
* @param string $attribute Le slug de l'Attribut
|
||||
* @param string $value Le slug de la valeur de l'Attribut
|
||||
*/
|
||||
public function __construct(
|
||||
public string $attribute,
|
||||
public string $value,
|
||||
) {}
|
||||
final readonly class ProductVariationAttribute {
|
||||
/**
|
||||
* @param string $attribute Le slug de l'Attribut
|
||||
* @param string $value Le slug de la valeur de l'Attribut
|
||||
*/
|
||||
public function __construct(
|
||||
public string $attribute,
|
||||
public string $value,
|
||||
) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ declare(strict_types=1);
|
|||
namespace HaikuAtelier;
|
||||
|
||||
// Désactive divers transformations du contenu par WordPress
|
||||
function desactive_wpautop(): void
|
||||
{
|
||||
remove_filter('the_content', 'wpautop');
|
||||
function desactive_wpautop(): void {
|
||||
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
|
||||
*/
|
||||
function desactive_transformation_contenu_tinymce(array $configuration): array
|
||||
{
|
||||
// Ne supprime pas les retours à la ligne
|
||||
$configuration['remove_linebreaks'] = false;
|
||||
// Convertis les caractères de retours à la ligne en <br>
|
||||
$configuration['convert_newlines_to_brs'] = true;
|
||||
// Supprime les <br> redondants
|
||||
$configuration['remove_redundant_brs'] = false;
|
||||
function desactive_transformation_contenu_tinymce(array $configuration): array {
|
||||
// Ne supprime pas les retours à la ligne
|
||||
$configuration['remove_linebreaks'] = false;
|
||||
// Convertis les caractères de retours à la ligne en <br>
|
||||
$configuration['convert_newlines_to_brs'] = true;
|
||||
// Supprime les <br> redondants
|
||||
$configuration['remove_redundant_brs'] = false;
|
||||
|
||||
// Retourne $configuration à WordPress
|
||||
return $configuration;
|
||||
// Retourne $configuration à WordPress
|
||||
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
|
||||
*/
|
||||
function autorise_import_svg_mediatheque(array $file_types): array
|
||||
{
|
||||
$new_filetypes = [];
|
||||
$new_filetypes['svg'] = 'image/svg+xml';
|
||||
function autorise_import_svg_mediatheque(array $file_types): array {
|
||||
$new_filetypes = [];
|
||||
$new_filetypes['svg'] = 'image/svg+xml';
|
||||
|
||||
return [...$file_types, ...$new_filetypes];
|
||||
return [...$file_types, ...$new_filetypes];
|
||||
}
|
||||
|
||||
function retire_motifs_blocs_gutenberg(): void
|
||||
{
|
||||
remove_theme_support('core-block-patterns');
|
||||
function retire_motifs_blocs_gutenberg(): void {
|
||||
remove_theme_support('core-block-patterns');
|
||||
}
|
||||
|
||||
function retire_styles_core_block(): void
|
||||
{
|
||||
wp_dequeue_style('core-block-supports');
|
||||
function retire_styles_core_block(): void {
|
||||
wp_dequeue_style('core-block-supports');
|
||||
}
|
||||
|
||||
// Désactive les appels à l'API de la mise à jour des traductions
|
||||
|
|
|
|||
|
|
@ -14,33 +14,32 @@ use function register_taxonomy;
|
|||
/**
|
||||
* Enregistre la Taxonomie « Collection ».
|
||||
*/
|
||||
function enregistre_taxonomie_collection(): void
|
||||
{
|
||||
$labels = [
|
||||
'add_new_item' => __('Add New Collection'),
|
||||
'all_items' => __('All Collections'),
|
||||
'edit_item' => __('Edit Collection'),
|
||||
'menu_name' => __('Collections'),
|
||||
'name' => __('Collections'),
|
||||
'new_item_name' => __('New Collection Name'),
|
||||
'search_items' => __('Search Collections'),
|
||||
'singular_name' => __('Collection'),
|
||||
'update_item' => __('Update Collection'),
|
||||
];
|
||||
$args = [
|
||||
'description' => __('An ensemble of pieces thematically or chronologically grouped together.'),
|
||||
'hierarchical' => false,
|
||||
'labels' => $labels,
|
||||
'publicly_queryable' => false,
|
||||
'query_var' => true,
|
||||
'rewrite' => ['slug' => 'collection'],
|
||||
'show_admin_column' => true,
|
||||
'show_in_menu' => true,
|
||||
'show_in_quick_edit' => true,
|
||||
'show_ui' => true,
|
||||
];
|
||||
function enregistre_taxonomie_collection(): void {
|
||||
$labels = [
|
||||
'add_new_item' => __('Add New Collection'),
|
||||
'all_items' => __('All Collections'),
|
||||
'edit_item' => __('Edit Collection'),
|
||||
'menu_name' => __('Collections'),
|
||||
'name' => __('Collections'),
|
||||
'new_item_name' => __('New Collection Name'),
|
||||
'search_items' => __('Search Collections'),
|
||||
'singular_name' => __('Collection'),
|
||||
'update_item' => __('Update Collection'),
|
||||
];
|
||||
$args = [
|
||||
'description' => __('An ensemble of pieces thematically or chronologically grouped together.'),
|
||||
'hierarchical' => false,
|
||||
'labels' => $labels,
|
||||
'publicly_queryable' => false,
|
||||
'query_var' => true,
|
||||
'rewrite' => ['slug' => 'collection'],
|
||||
'show_admin_column' => true,
|
||||
'show_in_menu' => true,
|
||||
'show_in_quick_edit' => true,
|
||||
'show_ui' => true,
|
||||
];
|
||||
|
||||
register_taxonomy('collection', ['product'], $args);
|
||||
register_taxonomy('collection', ['product'], $args);
|
||||
}
|
||||
|
||||
add_action('init', enregistre_taxonomie_collection(...));
|
||||
|
|
|
|||
|
|
@ -14,50 +14,46 @@ use function is_array;
|
|||
use function Psl\Option\none;
|
||||
use function Psl\Option\some;
|
||||
|
||||
final readonly class Post
|
||||
{
|
||||
/**
|
||||
* @return Option\Option<mixed>
|
||||
*/
|
||||
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);
|
||||
final readonly class Post {
|
||||
/**
|
||||
* @return Option\Option<mixed>
|
||||
*/
|
||||
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);
|
||||
|
||||
if ($value === false) {
|
||||
return none();
|
||||
}
|
||||
|
||||
return some($value);
|
||||
if ($value === false) {
|
||||
return none();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 some($value);
|
||||
}
|
||||
|
||||
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 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 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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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;
|
||||
|
|
@ -6,97 +6,28 @@ import {
|
|||
Effect,
|
||||
HashMap,
|
||||
Layer,
|
||||
ManagedRuntime,
|
||||
Option,
|
||||
pipe,
|
||||
Ref,
|
||||
Schema,
|
||||
SchemaIssue,
|
||||
Stream,
|
||||
} from "effect";
|
||||
import type { NonEmptyReadonlyArray } from "effect/Array";
|
||||
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 {
|
||||
ATTRIBUT_ARIA_CONTROLS,
|
||||
ATTRIBUT_ARIA_EXPANDED,
|
||||
ATTRIBUT_DESACTIVE,
|
||||
ATTRIBUT_HIDDEN,
|
||||
DOM_BOUTON_AJOUT_PANIER,
|
||||
DOM_BOUTONS_ACCORDEON,
|
||||
DOM_CONTENUS_ACCORDEON,
|
||||
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,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
} from "../constantes/dom.ts";
|
||||
import ProductPageElements from "./service-elements.ts";
|
||||
import type { DetailEnsemble } from "./types.d.ts";
|
||||
|
||||
class ProductPageDOM extends Context.Service<
|
||||
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.
|
||||
*/
|
||||
|
|
@ -110,26 +41,30 @@ class ProductPageDOM extends Context.Service<
|
|||
*/
|
||||
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>;
|
||||
/**
|
||||
* Met à jour l'état du Bouton d'ajout au Panier.
|
||||
*/
|
||||
onFormChangeHandler: (evt: Event) => Effect.Effect<void>;
|
||||
initPriceUpdatesOnVariationChange: () => Effect.Effect<void, NoSuchElementError | string>;
|
||||
/**
|
||||
* 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") {
|
||||
static readonly layer = Layer.effect(
|
||||
ProductPageDOM,
|
||||
Effect.gen(function*() {
|
||||
const { AddToCartButton, Details, ProductPrice, DetailsButtons, ProductRawJson, VariationChoiceForm, VariationSelectors } =
|
||||
yield* ProductPageElements;
|
||||
|
||||
const onFormChangeHandler = Effect.fnUntraced(function*(evt: Event) {
|
||||
const {
|
||||
AddToCartButton,
|
||||
Details,
|
||||
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.
|
||||
const target: HTMLFormElement = evt.target as HTMLFormElement;
|
||||
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
|
||||
evt.preventDefault();
|
||||
|
||||
|
|
@ -183,13 +120,67 @@ class ProductPageDOM extends Context.Service<
|
|||
return yield* Effect.void;
|
||||
});
|
||||
|
||||
const getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>> = () =>
|
||||
Effect.sync(() =>
|
||||
const ProductVariations: ReadonlyArray<ProductVariation> = yield* pipe(
|
||||
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) => ({
|
||||
attribute: select.id,
|
||||
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*() {
|
||||
/** 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(
|
||||
Stream.fromEventListener(VariationChoiceForm, "change"),
|
||||
Stream.tap(onVariationChangeHandler),
|
||||
Stream.fromEventListener(AddToCartButton, "click"),
|
||||
Stream.tap(onAddToCartButtonHandler),
|
||||
Stream.runDrain,
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const onVariationChangeHandler = Effect.fn("onVariationChangeHandler")(function*(){
|
||||
if (VariationChoiceForm.checkValidity() === false) {
|
||||
return yield* Effect.void;
|
||||
}
|
||||
|
||||
const variations = JSON.parse(ProductRawJson.textContent)?.variations as ReadonlyArray<unknown>;
|
||||
const chosenAttributes = yield* getProductAttributesFromDOM();
|
||||
|
||||
const equivalence = FxArray.makeEquivalence<{attribute: string,value: string}>((a,b) => {
|
||||
return a.attribute === b.attribute && a.value === b.value;
|
||||
});
|
||||
const chosenVariation = yield* FxArray.findFirst(variations, variation => equivalence(variation.attributes, chosenAttributes));
|
||||
const newPrice = chosenVariation.price;
|
||||
|
||||
ProductPrice.textContent = `${newPrice}€`;
|
||||
return yield* Effect.void;
|
||||
});
|
||||
const initPriceUpdatesOnVariationChange = Effect.fn("initPriceUpdatesOnVariationChange")(
|
||||
function*(): Effect.fn.Return<void, NoSuchElementError | string> {
|
||||
return yield* pipe(
|
||||
Stream.fromEventListener(VariationChoiceForm, "change"),
|
||||
Stream.tap(onVariationChangeHandler),
|
||||
Stream.runDrain,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
const initDetailInteractions = Effect.fn("initDetailInteractions")(function*() {
|
||||
return yield* pipe(
|
||||
|
|
@ -256,26 +239,16 @@ class ProductPageDOM extends Context.Service<
|
|||
});
|
||||
|
||||
return ProductPageDOM.of({
|
||||
getProductAttributesFromDOM,
|
||||
CurrentVariation,
|
||||
ProductVariations,
|
||||
initAddToCartButtonClicks,
|
||||
initAddToCartButtonInitialState,
|
||||
initAddToCartButtonUpdates,
|
||||
initDetailInteractions,
|
||||
initPriceUpdatesOnVariationChange,
|
||||
onDetailButtonClickHandler,
|
||||
onFormChangeHandler,
|
||||
onVariationChangeHandler,
|
||||
toggleAllDetails,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const ProductPageRuntime = ManagedRuntime.make(
|
||||
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 };
|
||||
export default ProductPageDOM;
|
||||
|
|
@ -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;
|
||||
6
web/app/themes/haiku-atelier-2024/src/scripts/page-produit/types.d.ts
vendored
Normal file
6
web/app/themes/haiku-atelier-2024/src/scripts/page-produit/types.d.ts
vendored
Normal 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 };
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
// Scripts pour la Page Produit
|
||||
|
||||
import { pipe } from "@mobily/ts-belt";
|
||||
import { Console, Effect, pipe as epipe } from "effect";
|
||||
|
||||
|
||||
import { ProductPageRuntime } from "./scripts-page-produit-service.ts";
|
||||
import { Console, Effect } from "effect";
|
||||
import ProductPageRuntime from "./page-produit/runtime.ts";
|
||||
import ProductPageDOM from "./page-produit/service-dom.ts";
|
||||
|
||||
/** États utiles pour les scripts de la page. */
|
||||
type EtatsPage = {
|
||||
|
|
@ -17,31 +15,6 @@ type EtatsPage = {
|
|||
// @ts-expect-error -- États injectés par le modèle PHP
|
||||
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 => {
|
||||
// event.preventDefault();
|
||||
// console.debug("getAttributeValuesFromDom", getAttributesFromDom());
|
||||
|
|
@ -131,8 +104,24 @@ const updatePriceOnAttributeChange = (): void => {
|
|||
// };
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
ProductPageRuntime.runFork(pipe(initAddToCartButton(), Effect.tapCause(Console.error)));
|
||||
ProductPageRuntime.runFork(pipe(initAddToCartInteractionUpdates(), Effect.tapCause(Console.error)));
|
||||
ProductPageRuntime.runFork(pipe(initDetailInteractions(), Effect.tapCause(Console.error)));
|
||||
updatePriceOnAttributeChange();
|
||||
console.debug("oups");
|
||||
Effect.gen(function*() {
|
||||
const DOM = yield* ProductPageDOM;
|
||||
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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use Illuminate\Support\Str;
|
|||
use Timber\Timber;
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialise Timber
|
||||
|
|
@ -30,13 +30,13 @@ $commande = $order;
|
|||
$date = new Carbon($commande->get_date_created());
|
||||
|
||||
$email = [
|
||||
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
|
||||
'livraison' => [
|
||||
'transporteur' => Str::of($commande->get_shipping_method())->replace(' (Free)', ''),
|
||||
'numero_suivi' => blank($commande->get_meta('tracking_number'))
|
||||
? 'UNKNOWN_TRACKING_NUMBER'
|
||||
: $commande->get_meta('tracking_number'),
|
||||
],
|
||||
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
|
||||
'livraison' => [
|
||||
'transporteur' => Str::of($commande->get_shipping_method())->replace(' (Free)', ''),
|
||||
'numero_suivi' => blank($commande->get_meta('tracking_number'))
|
||||
? 'UNKNOWN_TRACKING_NUMBER'
|
||||
: $commande->get_meta('tracking_number'),
|
||||
],
|
||||
];
|
||||
|
||||
$context['commande'] = $email;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use Illuminate\Support\Str;
|
|||
use Timber\Timber;
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialise Timber
|
||||
|
|
@ -31,49 +31,44 @@ $commande = $order;
|
|||
$date = new Carbon($commande->get_date_created());
|
||||
|
||||
$email = [
|
||||
'adresses' => [
|
||||
'facturation' => $commande->get_address('billing'),
|
||||
'livraison' => $commande->get_address('shipping'),
|
||||
],
|
||||
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
|
||||
'livraison' => [
|
||||
'methode' => $commande->get_shipping_method(),
|
||||
'numero_suivi' => $commande->get_meta('tracking_number'),
|
||||
],
|
||||
'paiement' => ['methode' => ''],
|
||||
'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
|
||||
$produit = $article->get_product();
|
||||
'adresses' => ['facturation' => $commande->get_address('billing'), 'livraison' => $commande->get_address('shipping')],
|
||||
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
|
||||
'livraison' => [
|
||||
'methode' => $commande->get_shipping_method(),
|
||||
'numero_suivi' => $commande->get_meta('tracking_number'),
|
||||
],
|
||||
'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) {
|
||||
return [];
|
||||
}
|
||||
if (is_bool($produit) || $produit === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
// Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide
|
||||
'attribut' => $produit->is_type('variable')
|
||||
? collect($produit->get_attributes())
|
||||
->mapWithKeys(static fn($_atr, $cle): array => [
|
||||
'nom' => Str::lower(wc_attribute_label($cle, $produit)),
|
||||
'valeur' => $produit->get_attribute($cle),
|
||||
])
|
||||
->toArray()
|
||||
: [],
|
||||
'lien' => $produit->get_permalink(),
|
||||
'nom' => $produit->get_title(),
|
||||
'prix_total' => $article->get_total(),
|
||||
'quantite' => $article->get_quantity(),
|
||||
];
|
||||
}),
|
||||
'totaux' => [
|
||||
'sous_total_livraison' => '0' === $commande->get_shipping_total()
|
||||
? 'Free'
|
||||
: $commande->get_shipping_total() . '€',
|
||||
'sous_total_produits' => $commande->get_subtotal() . '€',
|
||||
'sous_total_reduction' => '0.00' === $commande->get_discount_total()
|
||||
? '0'
|
||||
: Number::format((float) $commande->get_discount_total(), maxPrecision: 2) . '€',
|
||||
'total' => Number::format((float) $commande->get_total(), maxPrecision: 2) . '€',
|
||||
],
|
||||
return [
|
||||
// Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide
|
||||
'attribut' => $produit->is_type('variable')
|
||||
? collect($produit->get_attributes())
|
||||
->mapWithKeys(static fn($_atr, $cle): array => [
|
||||
'nom' => Str::lower(wc_attribute_label($cle, $produit)),
|
||||
'valeur' => $produit->get_attribute($cle),
|
||||
])
|
||||
->toArray()
|
||||
: [],
|
||||
'lien' => $produit->get_permalink(),
|
||||
'nom' => $produit->get_title(),
|
||||
'prix_total' => $article->get_total(),
|
||||
'quantite' => $article->get_quantity(),
|
||||
];
|
||||
}),
|
||||
'totaux' => [
|
||||
'sous_total_livraison' => '0' === $commande->get_shipping_total() ? 'Free' : $commande->get_shipping_total() . '€',
|
||||
'sous_total_produits' => $commande->get_subtotal() . '€',
|
||||
'sous_total_reduction' => '0.00' === $commande->get_discount_total()
|
||||
? '0'
|
||||
: 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
|
||||
$email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()];
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use Illuminate\Support\Str;
|
|||
use Timber\Timber;
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// Initialise Timber
|
||||
|
|
@ -31,45 +31,40 @@ $commande = $order;
|
|||
$date = new Carbon($commande->get_date_created());
|
||||
|
||||
$email = [
|
||||
'adresses' => [
|
||||
'facturation' => $commande->get_address('billing'),
|
||||
'livraison' => $commande->get_address('shipping'),
|
||||
],
|
||||
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
|
||||
'paiement' => ['methode' => ''],
|
||||
'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
|
||||
$produit = $article->get_product();
|
||||
'adresses' => ['facturation' => $commande->get_address('billing'), 'livraison' => $commande->get_address('shipping')],
|
||||
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
|
||||
'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) {
|
||||
return [];
|
||||
}
|
||||
if (is_bool($produit) || $produit === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
// Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide
|
||||
'attribut' => $article->is_type('variable')
|
||||
? collect($produit->get_attributes())
|
||||
->mapWithKeys(static fn($_atr, $cle): array => [
|
||||
'nom' => Str::lower(wc_attribute_label($cle, $produit)),
|
||||
'valeur' => $produit->get_attribute($cle),
|
||||
])
|
||||
->toArray()
|
||||
: [],
|
||||
'lien' => $produit->get_permalink(),
|
||||
'nom' => $produit->get_title(),
|
||||
'prix_total' => $article->get_total(),
|
||||
'quantite' => $article->get_quantity(),
|
||||
];
|
||||
}),
|
||||
'totaux' => [
|
||||
'sous_total_livraison' => '0' === $commande->get_shipping_total()
|
||||
? 'Free'
|
||||
: $commande->get_shipping_total() . '€',
|
||||
'sous_total_produits' => $commande->get_subtotal() . '€',
|
||||
'sous_total_reduction' => '0.00' === $commande->get_discount_total()
|
||||
? '0'
|
||||
: Number::format((float) $commande->get_discount_total(), maxPrecision: 2) . '€',
|
||||
'total' => Number::format((float) $commande->get_total(), maxPrecision: 2) . '€',
|
||||
],
|
||||
return [
|
||||
// Récupère l'Attribut d'un Produit variable ou renvoie un tableau vide
|
||||
'attribut' => $article->is_type('variable')
|
||||
? collect($produit->get_attributes())
|
||||
->mapWithKeys(static fn($_atr, $cle): array => [
|
||||
'nom' => Str::lower(wc_attribute_label($cle, $produit)),
|
||||
'valeur' => $produit->get_attribute($cle),
|
||||
])
|
||||
->toArray()
|
||||
: [],
|
||||
'lien' => $produit->get_permalink(),
|
||||
'nom' => $produit->get_title(),
|
||||
'prix_total' => $article->get_total(),
|
||||
'quantite' => $article->get_quantity(),
|
||||
];
|
||||
}),
|
||||
'totaux' => [
|
||||
'sous_total_livraison' => '0' === $commande->get_shipping_total() ? 'Free' : $commande->get_shipping_total() . '€',
|
||||
'sous_total_produits' => $commande->get_subtotal() . '€',
|
||||
'sous_total_reduction' => '0.00' === $commande->get_discount_total()
|
||||
? '0'
|
||||
: 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
|
||||
$email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue