Upsells improved - Upsell script fields supported

This commit is contained in:
Mark Pinkster 2026-01-13 22:03:29 +01:00
parent db3b1f74d2
commit a748307a2e
3 changed files with 70 additions and 50 deletions

View File

@ -39,7 +39,8 @@ function handleGetProducts(): void
$log("WooCommerce client initialized"); $log("WooCommerce client initialized");
// Only fetch fields needed for telesales app // Only fetch fields needed for telesales app
$productFields = 'id,name,price,type,upsell_ids,cross_sell_ids'; // Include meta_data for ACF fields like upsell_informatie
$productFields = 'id,name,price,type,upsell_ids,cross_sell_ids,callcenter_upsell_info';
$variationFields = 'id,price,attributes'; $variationFields = 'id,price,attributes';
$products = $woocommerce->get('products', [ $products = $woocommerce->get('products', [
@ -70,6 +71,8 @@ function handleGetProducts(): void
foreach ($products as $product) { foreach ($products as $product) {
$productId = (int) $product->id; $productId = (int) $product->id;
$log("Product Info from WooCommerce", $product);
// Get variations for variable products (only needed fields) // Get variations for variable products (only needed fields)
$variation_details = []; $variation_details = [];
if ($product->type === 'variable') { if ($product->type === 'variable') {
@ -112,7 +115,8 @@ function handleGetProducts(): void
'variation_details' => $variation_details, 'variation_details' => $variation_details,
'cuw_ids' => $cuw_ids, 'cuw_ids' => $cuw_ids,
'recommended_ids' => $recommended_ids, 'recommended_ids' => $recommended_ids,
'bogo_rules' => $bogo_rules 'bogo_rules' => $bogo_rules,
'upsell_text' => $product->callcenter_upsell_info
]; ];
} }

View File

@ -23,7 +23,7 @@
<!-- Load JavaScript modules with cache busting --> <!-- Load JavaScript modules with cache busting -->
<script> <script>
// Change this version number to bust cache for all JS files // Change this version number to bust cache for all JS files
const APP_VERSION = '1.1'; const APP_VERSION = '1.1.1';
const scripts = [ const scripts = [
'js/services/api.js', 'js/services/api.js',
@ -200,7 +200,7 @@
<span x-text="p.name"></span> <span x-text="p.name"></span>
<span x-show="p.bogo_rules && p.bogo_rules.length > 0" <span x-show="p.bogo_rules && p.bogo_rules.length > 0"
x-text="p.bogo_rules[0]?.badge_text || p.bogo_rules[0]?.label" x-text="p.bogo_rules[0]?.badge_text || p.bogo_rules[0]?.label"
class="ml-2 px-2 py-1 bg-red-500 text-white text-[9px] font-black rounded-full uppercase"> class="ml-2 px-2 py-1 bg-red-500 text-white text-[9px] font-black rounded-full max-w-48 truncate uppercase">
</span> </span>
</div> </div>
</div> </div>
@ -253,39 +253,51 @@
Wellicht ook interessant</p> Wellicht ook interessant</p>
<template x-for="u in recommendedOptions" :key="u.id"> <template x-for="u in recommendedOptions" :key="u.id">
<!-- Product with BOGO discount - styled like action block --> <div>
<div x-show="getYProductBogoDiscount(u)" <!-- Product with BOGO discount - styled like action block -->
class="p-4 bg-gradient-to-r from-red-500 to-pink-500 rounded-2xl text-white shadow-lg"> <div x-show="getYProductBogoDiscount(u)">
<div class="flex items-center justify-between gap-3"> <div class="p-4 bg-gradient-to-r from-red-500 to-pink-500 rounded-2xl text-white shadow-lg">
<div class="flex items-center gap-3"> <div class="flex items-center justify-between gap-3">
<div class="bg-white/20 p-2 rounded-full"> <div class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="bg-white/20 p-2 rounded-full">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7" /> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
</svg> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7" />
</div> </svg>
<div> </div>
<p class="font-black text-sm uppercase tracking-wide" x-text="u.name"></p> <div>
<p class="text-[10px] opacity-80" x-text="getYProductDiscountLabel(u)"></p> <p class="font-black text-sm uppercase tracking-wide" x-text="u.name"></p>
<p class="text-[10px] opacity-80" x-text="getYProductDiscountLabel(u)"></p>
</div>
</div>
<button @click="toggleUpsell(u)"
:class="isInCart(u.id) ? 'bg-slate-700' : 'bg-white'"
:disabled="isInCart(u.id)"
class="text-red-500 px-4 py-2 rounded-xl font-black text-xs uppercase shadow-md hover:bg-red-50 transition disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap">
<span x-text="isInCart(u.id) ? 'Toegevoegd' : ('+ €' + getYProductDiscountedPrice(u))"></span>
</button>
</div> </div>
</div> </div>
<button @click="toggleUpsell(u)" <!-- Upsell information text for BOGO products -->
:class="isInCart(u.id) ? 'bg-slate-700' : 'bg-white'" <div x-show="u.upsell_text && u.upsell_text.length > 0"
:disabled="isInCart(u.id)" x-html="u.upsell_text"
class="text-red-500 px-4 py-2 rounded-xl font-black text-xs uppercase shadow-md hover:bg-red-50 transition disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap"> class="text-xs text-slate-600 italic px-2 mt-2"></div>
<span x-text="isInCart(u.id) ? 'Toegevoegd' : ('+ €' + getYProductDiscountedPrice(u))"></span>
</button>
</div> </div>
</div>
<!-- Regular product without BOGO discount --> <!-- Regular product without BOGO discount -->
<div x-show="!getYProductBogoDiscount(u)" <div x-show="!getYProductBogoDiscount(u)">
class="flex items-center justify-between p-4 border rounded-2xl bg-slate-50 hover:bg-white transition-all shadow-sm"> <div class="flex items-center justify-between p-4 border rounded-2xl bg-slate-50 hover:bg-white transition-all shadow-sm">
<span class="text-xs font-bold text-slate-700" <span class="text-xs font-bold text-slate-700"
x-text="u.name + ' (€' + u.price + ')'"></span> x-text="u.name + ' (€' + u.price + ')'"></span>
<button @click="toggleUpsell(u)" :class="isInCart(u.id) ? 'bg-red-500' : 'bg-green-600'" <button @click="toggleUpsell(u)" :class="isInCart(u.id) ? 'bg-red-500' : 'bg-green-600'"
class="text-white px-6 py-2 rounded-xl text-[10px] font-black uppercase shadow-md" class="text-white px-6 py-2 rounded-xl text-[10px] font-black uppercase shadow-md"
x-text="isInCart(u.id) ? 'Verwijder' : 'Voeg toe'"> x-text="isInCart(u.id) ? 'Verwijder' : 'Voeg toe'">
</button> </button>
</div>
<!-- Upsell information text for regular products -->
<div x-show="u.upsell_text && u.upsell_text.length > 0"
x-html="u.upsell_text"
class="text-xs text-slate-600 italic px-2 mt-2"></div>
</div>
</div> </div>
</template> </template>
</div> </div>

View File

@ -196,6 +196,7 @@ const BogoService = {
/** /**
* Get BOGO discount info for a Y product based on X product's rules * Get BOGO discount info for a Y product based on X product's rules
* Checks ALL BOGO rules on the X product, not just the primary one
* *
* @param {Object} xProduct - The main product (X) with BOGO rules * @param {Object} xProduct - The main product (X) with BOGO rules
* @param {Object} yProduct - The upsell product (Y) to check * @param {Object} yProduct - The upsell product (Y) to check
@ -204,23 +205,26 @@ const BogoService = {
getYProductDiscount(xProduct, yProduct) { getYProductDiscount(xProduct, yProduct) {
if (!xProduct || !this.hasBogoRules(xProduct)) return null; if (!xProduct || !this.hasBogoRules(xProduct)) return null;
const rule = this.getPrimaryBogoRule(xProduct); // Check ALL BOGO rules, not just the primary one
for (const rule of xProduct.bogo_rules) {
// Only for buy_x_get_y rules
if (rule.type !== 'buy_x_get_y') continue;
// Only for buy_x_get_y rules // Check if yProduct is in the get_product_ids
if (rule.type !== 'buy_x_get_y') return null; if (!rule.get_product_ids || !rule.get_product_ids.includes(parseInt(yProduct.id))) {
continue;
}
// Check if yProduct is in the get_product_ids // Return discount info for the first matching rule
if (!rule.get_product_ids || !rule.get_product_ids.includes(parseInt(yProduct.id))) { return {
return null; rule_id: rule.rule_id,
discount_type: rule.discount_type,
discount_value: rule.discount_value,
label: rule.label,
badge_text: rule.badge_text
};
} }
// Return discount info return null;
return {
rule_id: rule.rule_id,
discount_type: rule.discount_type,
discount_value: rule.discount_value,
label: rule.label,
badge_text: rule.badge_text
};
} }
}; };