Stock Status Widget pour Elementor

Un widget Elementor léger qui interroge le statut stock du produit en cours de boucle et affiche un badge conditionnel (visible uniquement quand le produit est hors stock, ou configurable pour afficher tous les statuts).

Deux modes d’affichage configurables dans les contrôles du widget : soit uniquement visible quand le produit est en rupture/réapprovisionnement (mode par défaut, idéal pour les cards), soit toujours visible avec les 4 statuts (en stock, stock faible, réappro, rupture).

Détection intelligente du stock faible : il croise get_stock_status() avec get_stock_quantity() et le seuil woocommerce_notify_low_stock_amount pour distinguer un produit « en stock » d’un produit « stock faible » — ce que WooCommerce ne fait pas nativement dans son statut.

Contrôles Elementor complets dans l’onglet Style : typographie, couleurs texte/fond par statut (via des onglets séparés), padding, border-radius, alignement, taille et espacement de l’icône. Tout est piloté par les selectors Elementor, donc ça fonctionne en responsive et dans le preview éditeur.

Compatibilité Loop Builder : le widget récupère le produit courant via wc_get_product( get_the_ID() ) en fallback du global $product, et affiche un aperçu « Rupture de stock » dans l’éditeur quand il n’y a pas de produit en contexte.

Pour l’installer : dépose le fichier dans wp-content/mu-plugins/.
Le widget apparaîtra dans la catégorie « WooCommerce » d’Elementor sous le nom « Wycan – Statut Stock », prêt à être glissé dans le template Loop Builder.

				
					<?php
/**
 * Plugin Name: Wycan - Stock Status Widget for Elementor
 * Description: Widget Elementor affichant le statut de stock des produits WooCommerce. Conçu pour le Loop Builder (cards produit dans les archives/catégories).
 * Version: 1.0.0
 * Author: Wycan
 * Author URI: https://wycan.fr
 * Requires Plugins: elementor, woocommerce
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Enregistrement du widget auprès d'Elementor
 */
add_action( 'elementor/widgets/register', function ( $widgets_manager ) {

	if ( ! class_exists( 'WooCommerce' ) ) {
		return;
	}

	class Wycan_Stock_Status_Widget extends \Elementor\Widget_Base {

		public function get_name() {
			return 'wycan-stock-status';
		}

		public function get_title() {
			return 'Wycan – Statut Stock';
		}

		public function get_icon() {
			return 'eicon-info-circle-o';
		}

		public function get_categories() {
			return [ 'woocommerce-elements', 'general' ];
		}

		public function get_keywords() {
			return [ 'stock', 'rupture', 'disponibilité', 'woocommerce', 'wycan' ];
		}

		/* =====================================================================
		   CONTRÔLES ELEMENTOR
		   ===================================================================== */
		protected function register_controls() {

			/* ---- Section : Contenu ---- */
			$this->start_controls_section( 'section_content', [
				'label' => 'Contenu',
				'tab'   => \Elementor\Controls_Manager::TAB_CONTENT,
			] );

			$this->add_control( 'display_mode', [
				'label'   => 'Affichage',
				'type'    => \Elementor\Controls_Manager::SELECT,
				'default' => 'out_of_stock_only',
				'options' => [
					'out_of_stock_only' => 'Uniquement si rupture de stock',
					'always'            => 'Toujours (tous les statuts)',
				],
			] );

			$this->add_control( 'label_in_stock', [
				'label'     => 'Libellé – En stock',
				'type'      => \Elementor\Controls_Manager::TEXT,
				'default'   => 'En stock',
				'condition' => [ 'display_mode' => 'always' ],
			] );

			$this->add_control( 'label_low_stock', [
				'label'     => 'Libellé – Stock faible',
				'type'      => \Elementor\Controls_Manager::TEXT,
				'default'   => 'Stock faible',
				'condition' => [ 'display_mode' => 'always' ],
			] );

			$this->add_control( 'label_on_backorder', [
				'label'   => 'Libellé – En réapprovisionnement',
				'type'    => \Elementor\Controls_Manager::TEXT,
				'default' => 'En réapprovisionnement',
			] );

			$this->add_control( 'label_out_of_stock', [
				'label'   => 'Libellé – Rupture de stock',
				'type'    => \Elementor\Controls_Manager::TEXT,
				'default' => 'Rupture de stock',
			] );

			$this->add_control( 'show_icon', [
				'label'        => 'Afficher une icône',
				'type'         => \Elementor\Controls_Manager::SWITCHER,
				'default'      => 'yes',
				'label_on'     => 'Oui',
				'label_off'    => 'Non',
			] );

			$this->end_controls_section();

			/* ---- Section : Style – Badge ---- */
			$this->start_controls_section( 'section_style_badge', [
				'label' => 'Badge',
				'tab'   => \Elementor\Controls_Manager::TAB_STYLE,
			] );

			$this->add_responsive_control( 'badge_alignment', [
				'label'   => 'Alignement',
				'type'    => \Elementor\Controls_Manager::CHOOSE,
				'options' => [
					'flex-start' => [ 'title' => 'Gauche',  'icon' => 'eicon-text-align-left' ],
					'center'     => [ 'title' => 'Centre',  'icon' => 'eicon-text-align-center' ],
					'flex-end'   => [ 'title' => 'Droite',  'icon' => 'eicon-text-align-right' ],
				],
				'default'   => 'flex-start',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status' => 'justify-content: {{VALUE}};',
				],
			] );

			$this->add_group_control( \Elementor\Group_Control_Typography::get_type(), [
				'name'     => 'badge_typography',
				'selector' => '{{WRAPPER}} .wycan-stock-status__badge',
			] );

			/* -- Onglets couleurs par statut -- */
			$this->start_controls_tabs( 'tabs_badge_colors' );

			// En stock
			$this->start_controls_tab( 'tab_in_stock', [
				'label'     => 'En stock',
				'condition' => [ 'display_mode' => 'always' ],
			] );
			$this->add_control( 'color_in_stock', [
				'label'     => 'Couleur texte',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#16a34a',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--instock' => 'color: {{VALUE}};',
				],
			] );
			$this->add_control( 'bg_in_stock', [
				'label'     => 'Couleur fond',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#dcfce7',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--instock' => 'background-color: {{VALUE}};',
				],
			] );
			$this->end_controls_tab();

			// Stock faible
			$this->start_controls_tab( 'tab_low_stock', [
				'label'     => 'Stock faible',
				'condition' => [ 'display_mode' => 'always' ],
			] );
			$this->add_control( 'color_low_stock', [
				'label'     => 'Couleur texte',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#ca8a04',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--lowstock' => 'color: {{VALUE}};',
				],
			] );
			$this->add_control( 'bg_low_stock', [
				'label'     => 'Couleur fond',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#fef9c3',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--lowstock' => 'background-color: {{VALUE}};',
				],
			] );
			$this->end_controls_tab();

			// Réapprovisionnement
			$this->start_controls_tab( 'tab_backorder', [ 'label' => 'Réappro.' ] );
			$this->add_control( 'color_backorder', [
				'label'     => 'Couleur texte',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#2563eb',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--onbackorder' => 'color: {{VALUE}};',
				],
			] );
			$this->add_control( 'bg_backorder', [
				'label'     => 'Couleur fond',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#dbeafe',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--onbackorder' => 'background-color: {{VALUE}};',
				],
			] );
			$this->end_controls_tab();

			// Rupture
			$this->start_controls_tab( 'tab_out_of_stock', [ 'label' => 'Rupture' ] );
			$this->add_control( 'color_out_of_stock', [
				'label'     => 'Couleur texte',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#dc2626',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--outofstock' => 'color: {{VALUE}};',
				],
			] );
			$this->add_control( 'bg_out_of_stock', [
				'label'     => 'Couleur fond',
				'type'      => \Elementor\Controls_Manager::COLOR,
				'default'   => '#fee2e2',
				'selectors' => [
					'{{WRAPPER}} .wycan-stock-status__badge--outofstock' => 'background-color: {{VALUE}};',
				],
			] );
			$this->end_controls_tab();

			$this->end_controls_tabs();

			$this->add_responsive_control( 'badge_padding', [
				'label'      => 'Padding',
				'type'       => \Elementor\Controls_Manager::DIMENSIONS,
				'size_units' => [ 'px', 'em', '%' ],
				'default'    => [
					'top'    => '4',
					'right'  => '10',
					'bottom' => '4',
					'left'   => '10',
					'unit'   => 'px',
				],
				'selectors'  => [
					'{{WRAPPER}} .wycan-stock-status__badge' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				],
				'separator' => 'before',
			] );

			$this->add_control( 'badge_border_radius', [
				'label'      => 'Border Radius',
				'type'       => \Elementor\Controls_Manager::DIMENSIONS,
				'size_units' => [ 'px', '%' ],
				'default'    => [
					'top'    => '4',
					'right'  => '4',
					'bottom' => '4',
					'left'   => '4',
					'unit'   => 'px',
				],
				'selectors'  => [
					'{{WRAPPER}} .wycan-stock-status__badge' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				],
			] );

			$this->end_controls_section();

			/* ---- Section : Style – Icône ---- */
			$this->start_controls_section( 'section_style_icon', [
				'label'     => 'Icône',
				'tab'       => \Elementor\Controls_Manager::TAB_STYLE,
				'condition' => [ 'show_icon' => 'yes' ],
			] );

			$this->add_responsive_control( 'icon_size', [
				'label'      => 'Taille',
				'type'       => \Elementor\Controls_Manager::SLIDER,
				'size_units' => [ 'px' ],
				'range'      => [ 'px' => [ 'min' => 8, 'max' => 32 ] ],
				'default'    => [ 'size' => 14, 'unit' => 'px' ],
				'selectors'  => [
					'{{WRAPPER}} .wycan-stock-status__icon svg' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};',
				],
			] );

			$this->add_responsive_control( 'icon_gap', [
				'label'      => 'Espacement',
				'type'       => \Elementor\Controls_Manager::SLIDER,
				'size_units' => [ 'px' ],
				'range'      => [ 'px' => [ 'min' => 0, 'max' => 20 ] ],
				'default'    => [ 'size' => 6, 'unit' => 'px' ],
				'selectors'  => [
					'{{WRAPPER}} .wycan-stock-status__badge' => 'gap: {{SIZE}}{{UNIT}};',
				],
			] );

			$this->end_controls_section();
		}

		/* =====================================================================
		   RENDU
		   ===================================================================== */
		protected function render() {

			global $product;

			// Compatibilité Loop Builder : récupérer le produit courant
			if ( ! $product instanceof \WC_Product ) {
				$product = wc_get_product( get_the_ID() );
			}

			if ( ! $product instanceof \WC_Product ) {
				if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
					echo '<div class="wycan-stock-status"><span class="wycan-stock-status__badge wycan-stock-status__badge--outofstock">Rupture de stock (aperçu)</span></div>';
				}
				return;
			}

			$settings     = $this->get_settings_for_display();
			$stock_status = $product->get_stock_status(); // instock | outofstock | onbackorder
			$stock_qty    = $product->get_stock_quantity();
			$low_amount   = absint( get_option( 'woocommerce_notify_low_stock_amount', 2 ) );

			// Détection stock faible
			$is_low_stock = ( 'instock' === $stock_status && $product->managing_stock() && null !== $stock_qty && $stock_qty <= $low_amount && $stock_qty > 0 );

			$resolved_status = $is_low_stock ? 'lowstock' : $stock_status;

			// Mode "rupture uniquement" : ne rien afficher si en stock / stock faible
			if ( 'out_of_stock_only' === $settings['display_mode'] && ! in_array( $resolved_status, [ 'outofstock', 'onbackorder' ], true ) ) {
				return;
			}

			// Mapping libellé / classe CSS / icône SVG
			$map = [
				'instock'     => [
					'label' => $settings['label_in_stock'] ?? 'En stock',
					'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',
				],
				'lowstock'    => [
					'label' => $settings['label_low_stock'] ?? 'Stock faible',
					'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
				],
				'onbackorder' => [
					'label' => $settings['label_on_backorder'] ?? 'En réapprovisionnement',
					'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>',
				],
				'outofstock'  => [
					'label' => $settings['label_out_of_stock'] ?? 'Rupture de stock',
					'icon'  => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
				],
			];

			if ( ! isset( $map[ $resolved_status ] ) ) {
				return;
			}

			$data  = $map[ $resolved_status ];
			$class = 'wycan-stock-status__badge wycan-stock-status__badge--' . esc_attr( $resolved_status );

			echo '<div class="wycan-stock-status">';
			echo '<span class="' . $class . '">';

			if ( 'yes' === $settings['show_icon'] ) {
				echo '<span class="wycan-stock-status__icon">' . $data['icon'] . '</span>';
			}

			echo '<span class="wycan-stock-status__label">' . esc_html( $data['label'] ) . '</span>';
			echo '</span>';
			echo '</div>';
		}

		protected function content_template() {
			// Rendu côté serveur uniquement (données WooCommerce dynamiques)
		}
	}

	$widgets_manager->register( new Wycan_Stock_Status_Widget() );
} );

/**
 * Styles de base (inline, pas de fichier CSS externe)
 */
add_action( 'wp_head', function () {
	if ( ! class_exists( 'WooCommerce' ) ) {
		return;
	}
	?>
	<style id="wycan-stock-status-css">
		.wycan-stock-status {
			display: flex;
			line-height: 1;
		}
		.wycan-stock-status__badge {
			display: inline-flex;
			align-items: center;
			gap: 6px;
			font-size: 13px;
			font-weight: 600;
			line-height: 1;
			white-space: nowrap;
			padding: 4px 10px;
			border-radius: 4px;
			transition: opacity .2s ease;
		}
		.wycan-stock-status__icon {
			display: inline-flex;
			flex-shrink: 0;
		}
		.wycan-stock-status__icon svg {
			width: 14px;
			height: 14px;
			display: block;
		}
		/* Couleurs par défaut (surchargées par les contrôles Elementor) */
		.wycan-stock-status__badge--instock {
			color: #16a34a;
			background-color: #dcfce7;
		}
		.wycan-stock-status__badge--lowstock {
			color: #ca8a04;
			background-color: #fef9c3;
		}
		.wycan-stock-status__badge--onbackorder {
			color: #2563eb;
			background-color: #dbeafe;
		}
		.wycan-stock-status__badge--outofstock {
			color: #dc2626;
			background-color: #fee2e2;
		}
	</style>
	<?php
} );

/**
 * Style éditeur Elementor (preview iframe)
 */
add_action( 'elementor/editor/after_enqueue_styles', function () {
	wp_add_inline_style( 'elementor-editor', '
		.wycan-stock-status { display: flex; line-height: 1; }
		.wycan-stock-status__badge { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; padding: 4px 10px; border-radius: 4px; }
		.wycan-stock-status__badge--outofstock { color: #dc2626; background-color: #fee2e2; }
	' );
} );

				
			
Envie de laisser un commentaire ?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Tu veux voir d'autres bouts de code ?

Bouton retour en haut pour Elementor

Bouton « Retour en haut » avec remplissage progressif en fonction du défilement. Utilise automatiquement la couleur principale d'Elementor.

Stock Status Widget pour Elementor

Widget Elementor affichant le statut de stock des produits WooCommerce. Conçu pour le Loop Builder (cards produit dans les archives/catégories).

Menu sticky intelligent (hide on scroll)

Ce snippet met en place un menu sticky fixé en haut de l’écran

Méthode d’expédition gratuite avec coupon WooCommerce

Offrir les frais de livraison… mais seulement pour la méthode d’expédition que tu choisis.

WooCommerce Product Buyers List

Ajoute un onglet dans WooCommerce pour afficher les acheteurs d'un produit spécifique avec filtre de date.

Emails additionnels par produits WooCommerce

Ajoute un onglet personnalisé dans les produits WooCommerce pour écrire un e-mail spécifique à chaque produit. Celui-ci sera envoyé dans un e-mail dédié.
ON DISCUTE ?

Une question sur WordPress ?
Un projet web à faire sous traiter par un freelance ?
Je suis votre homme !