Explorez notre Univers.
Des produits d'exception et des services exclusifs réunis en un seul endroit.
Affichage :
Catégorie
Prix
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE PELLUCHE REF TT25011"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-pelluche-ref-tt25011-xtxji",
name: "TROUSSE PELLUCHE REF TT25011",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/AzTj4xPr-1783607269.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/AzTj4xPr-1783607269.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+PELLUCHE+REF+TT25011",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/AzTj4xPr-1783607269.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE PELLUCHE REF TT25011",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "BANANA REF U2056"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/banana-ref-u2056-zfld8",
name: "BANANA REF U2056",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/rlbthOcK-1783607410.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/rlbthOcK-1783607410.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "BANANA",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+BANANA+REF+U2056",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/rlbthOcK-1783607410.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "BANANA REF U2056",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SUPPORT CHARIOT REF WL17-18"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/support-chariot-ref-wl17-18-r5cqn",
name: "SUPPORT CHARIOT REF WL17-18",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/4HUEpwZk-1783607479.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/4HUEpwZk-1783607479.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SUPPORT CHARIOT",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SUPPORT+CHARIOT+REF+WL17-18",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/4HUEpwZk-1783607479.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SUPPORT CHARIOT REF WL17-18",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SUPPORT CHARIOT REF WL17-18
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM24-Y001"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-fm24-y001-u8ris",
name: "TROUSSE REF FM24-Y001",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/RvFWHHiZ-1783607944.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/RvFWHHiZ-1783607944.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+FM24-Y001",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/RvFWHHiZ-1783607944.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM24-Y001",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SERVIETTE REF TT25002"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/serviette-ref-tt25002-h3uwe",
name: "SERVIETTE REF TT25002",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/vsXlwUYC-1783607234.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/vsXlwUYC-1783607234.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SERVIETTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SERVIETTE+REF+TT25002",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/vsXlwUYC-1783607234.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SERVIETTE REF TT25002",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF R2"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-r2-mi50y",
name: "TROUSSE REF R2",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Ht8yWIPr-1783607446.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Ht8yWIPr-1783607446.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+R2",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Ht8yWIPr-1783607446.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF R2",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAUX REF N60\/90"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/tableaux-ref-n6090-n4sud",
name: "TABLEAUX REF N60\/90",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/ZCazzi6R-1783607961.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/ZCazzi6R-1783607961.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "TABLEAUX",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TABLEAUX+REF+N60%2F90",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/ZCazzi6R-1783607961.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAUX REF N60\/90",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAUX REF N90\/120"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/tableaux-ref-n90120-bct9m",
name: "TABLEAUX REF N90\/120",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/mS5owy9z-1783607962.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/mS5owy9z-1783607962.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "TABLEAUX",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TABLEAUX+REF+N90%2F120",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/mS5owy9z-1783607962.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAUX REF N90\/120",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF P003"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-p003-7ks7j",
name: "TROUSSE REF P003",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/I1UCF37k-1783607427.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/I1UCF37k-1783607427.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+P003",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/I1UCF37k-1783607427.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF P003",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF P007"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-p007-o1jtx",
name: "TROUSSE REF P007",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/GuvD6xsu-1783607431.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/GuvD6xsu-1783607431.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+P007",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/GuvD6xsu-1783607431.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF P007",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF P019"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-p019-phjdn",
name: "TROUSSE REF P019",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/X4gz7UbX-1783607442.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/X4gz7UbX-1783607442.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+P019",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/X4gz7UbX-1783607442.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF P019",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SUPPORT CHARIOT REF F27"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/support-chariot-ref-f27-nmiok",
name: "SUPPORT CHARIOT REF F27",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/ZyVOZsua-1783607476.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/ZyVOZsua-1783607476.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SUPPORT CHARIOT",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SUPPORT+CHARIOT+REF+F27",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/ZyVOZsua-1783607476.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SUPPORT CHARIOT REF F27",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SUPPORT CHARIOT REF F27
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM25002"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-fm25002-tmgqp",
name: "TROUSSE REF FM25002",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/YcM0d4gH-1783607260.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/YcM0d4gH-1783607260.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+FM25002",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/YcM0d4gH-1783607260.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM25002",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF FM25006"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-a-dos-ref-fm25006-gdj8v",
name: "SAC A DOS REF FM25006",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Cn6U4Cxm-1783607233.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Cn6U4Cxm-1783607233.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC A DOS",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+A+DOS+REF+FM25006",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Cn6U4Cxm-1783607233.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF FM25006",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF FM25007"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-de-gouter-ref-fm25007-i89fc",
name: "SAC DE GOUTER REF FM25007",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/uA5kPsg5-1783607461.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/uA5kPsg5-1783607461.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC DE GOUTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+DE+GOUTER+REF+FM25007",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/uA5kPsg5-1783607461.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF FM25007",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SAC DE GOUTER REF FM25007
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF FM25008"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-de-gouter-ref-fm25008-xrekc",
name: "SAC DE GOUTER REF FM25008",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/p9VfWBjO-1783607465.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/p9VfWBjO-1783607465.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC DE GOUTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+DE+GOUTER+REF+FM25008",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/p9VfWBjO-1783607465.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF FM25008",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SAC DE GOUTER REF FM25008
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF FM25009"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-de-gouter-ref-fm25009-sq3c8",
name: "SAC DE GOUTER REF FM25009",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/DRbMTJbm-1783607469.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/DRbMTJbm-1783607469.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC DE GOUTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+DE+GOUTER+REF+FM25009",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/DRbMTJbm-1783607469.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF FM25009",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SAC DE GOUTER REF FM25009
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM25012"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-fm25012-jxw1n",
name: "TROUSSE REF FM25012",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/1pr9CLyS-1783607319.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/1pr9CLyS-1783607319.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+FM25012",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/1pr9CLyS-1783607319.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM25012",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM25016"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-fm25016-7igds",
name: "TROUSSE REF FM25016",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/9QR1YzVt-1783607275.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/9QR1YzVt-1783607275.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+FM25016",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/9QR1YzVt-1783607275.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF FM25016",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF H"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-h-2jdp0",
name: "TROUSSE REF H",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/c2sAsqmP-1783607300.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/c2sAsqmP-1783607300.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+H",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/c2sAsqmP-1783607300.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF H",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF BS01-14"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-a-dos-ref-bs01-14-gop4m",
name: "SAC A DOS REF BS01-14",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/AFxTrRcd-1783607475.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/AFxTrRcd-1783607475.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC A DOS",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+A+DOS+REF+BS01-14",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/AFxTrRcd-1783607475.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF BS01-14",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAU REF 90\/120"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/tableau-ref-90120-r6chu",
name: "TABLEAU REF 90\/120",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Bcqx9uXG-1783607965.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Bcqx9uXG-1783607965.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "TABLEAUX",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TABLEAU+REF+90%2F120",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/Bcqx9uXG-1783607965.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAU REF 90\/120",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 599"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-599-jmx1z",
name: "TROUSSE REF 599",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/vZ2Dp2OV-1783607394.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/vZ2Dp2OV-1783607394.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+599",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/vZ2Dp2OV-1783607394.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 599",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAU REF 60\/90"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/tableau-ref-6090-mwrzb",
name: "TABLEAU REF 60\/90",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/S3sDkB5h-1783607964.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/S3sDkB5h-1783607964.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "TABLEAUX",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TABLEAU+REF+60%2F90",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/S3sDkB5h-1783607964.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TABLEAU REF 60\/90",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 3005"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-3005-ciodb",
name: "TROUSSE REF 3005",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/2C7SnJaz-1783607418.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/2C7SnJaz-1783607418.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+3005",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/2C7SnJaz-1783607418.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 3005",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 32003"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-a-dos-ref-32003-pgzqb",
name: "SAC A DOS REF 32003",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/6mx8b4VT-1783607256.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/6mx8b4VT-1783607256.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC A DOS",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+A+DOS+REF+32003",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/6mx8b4VT-1783607256.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 32003",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 1814"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-a-dos-ref-1814-xzhx6",
name: "SAC A DOS REF 1814",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/zl3wXETv-1783607231.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/zl3wXETv-1783607231.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC A DOS",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+A+DOS+REF+1814",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/zl3wXETv-1783607231.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 1814",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "Sac de gouter ref 184"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-de-gouter-ref-184-wlxcg",
name: "Sac de gouter ref 184",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/j9fiAEwA-1783607237.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/j9fiAEwA-1783607237.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC DE GOUTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+Sac+de+gouter+ref+184",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/j9fiAEwA-1783607237.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "Sac de gouter ref 184",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
Sac de gouter ref 184
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF 192"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-de-gouter-ref-192-yrvwz",
name: "SAC DE GOUTER REF 192",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/UPLIupkf-1783607242.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/UPLIupkf-1783607242.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC DE GOUTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+DE+GOUTER+REF+192",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/UPLIupkf-1783607242.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF 192",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SAC DE GOUTER REF 192
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF 193"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-de-gouter-ref-193-hauwk",
name: "SAC DE GOUTER REF 193",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/8YtWSGU1-1783607251.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/8YtWSGU1-1783607251.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC DE GOUTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+DE+GOUTER+REF+193",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/8YtWSGU1-1783607251.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC DE GOUTER REF 193",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
Vite, plus que
Aucun avis
En Rupture
SAC DE GOUTER REF 193
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 3001"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-3001-iuyoe",
name: "TROUSSE REF 3001",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/sscqytnx-1783607321.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/sscqytnx-1783607321.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+3001",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/sscqytnx-1783607321.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 3001",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 3002"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-3002-rpsop",
name: "TROUSSE REF 3002",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/HbpQUMyK-1783607325.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/HbpQUMyK-1783607325.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+3002",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/HbpQUMyK-1783607325.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 3002",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 102"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-a-dos-ref-102-upiww",
name: "SAC A DOS REF 102",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/cuPhagmv-1783607459.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/cuPhagmv-1783607459.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC A DOS",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+A+DOS+REF+102",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/cuPhagmv-1783607459.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 102",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 104-8.5"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-104-85-3rw8u",
name: "TROUSSE REF 104-8.5",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/DQkaTB5j-1783607956.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/DQkaTB5j-1783607956.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+104-8.5",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/DQkaTB5j-1783607956.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 104-8.5",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SERVIETTE REF 106"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/serviette-ref-106-u9ela",
name: "SERVIETTE REF 106",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/0abJj0Jl-1783607460.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/0abJj0Jl-1783607460.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SERVIETTE",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SERVIETTE+REF+106",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/0abJj0Jl-1783607460.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SERVIETTE REF 106",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 107-8.5"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/trousse-ref-107-85-y7gbu",
name: "TROUSSE REF 107-8.5",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/zrYCnATV-1783607957.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/zrYCnATV-1783607957.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "Trousse",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+TROUSSE+REF+107-8.5",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/zrYCnATV-1783607957.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "TROUSSE REF 107-8.5",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
0 && this.currentStock <= 5; },
get shouldForceQuote() { return this.isQuoteMode || (this.isService && this.currentPrice <= 0); },
get displaySku() {
return (this.selectedVariant && this.selectedVariant.sku) ? this.selectedVariant.sku : null;
},
get activeVariantLabel() {
if (!this.selectedVariant) return '';
return this.selectedVariant.label || this.selectedVariant.name || '';
},
init() {
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
this.inWishlist = wl.includes(this.id);
}
window.addEventListener('wishlist-updated', (e) => {
if (e.detail.product_id === this.id) {
this.inWishlist = e.detail.added;
}
});
},
get currentPrice() {
let p = this.selectedVariant ? parseFloat(this.selectedVariant.price) : this.basePrice;
return p > 0 ? p : this.basePrice;
},
formatPrice(p) { return Number(p).toLocaleString('fr-TN', { minimumFractionDigits: 3, maximumFractionDigits: 3, useGrouping: false }); },
handleCardNavigation(event) {
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
event.preventDefault();
if (typeof window.smartTrack === 'function') {
window.smartTrack(this.isService ? 'service_click' : 'product_click', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 1628"
});
}
this.openQuickView();
},
openQuickView() {
const activeGallery = this.galleryImages && this.galleryImages.length ? this.galleryImages : [this.activeImage].filter(Boolean);
const mappedVariants = (this.variants || []).map(v => ({
...v,
price_formatted: this.formatPrice(v.price || 0)
}));
window.dispatchEvent(new CustomEvent('open-product-quickview', {
detail: {
id: this.id,
url: "https:\/\/creationhappy.smartbs.tn\/catalogue\/produit\/sac-a-dos-ref-1628-9rr5k",
name: "SAC A DOS REF 1628",
summary: "",
type: this.isService ? 'service' : 'product',
is_service: this.isService,
is_quote_mode: this.isQuoteMode,
should_force_quote: this.shouldForceQuote,
manage_stock: this.productManageStock,
allow_backorder: this.allowBackorder,
stock: this.currentStock,
sku: this.displaySku,
unit: "pi\u00e8ce",
currency: "TND",
image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/hfBEvLwk-1783607474.webp",
active_image: this.activeImage || "https:\/\/creationhappy.smartbs.tn\/storage\/products\/hfBEvLwk-1783607474.webp",
gallery: activeGallery,
price: this.currentPrice,
price_formatted: this.formatPrice(this.currentPrice),
compare_price_formatted: "0,000",
seller: null,
category: "SAC A DOS",
wa_link: "https:\/\/wa.me\/33123456789?text=Bonjour%2C+je+suis+int%C3%A9ress%C3%A9+par+%3A+SAC+A+DOS+REF+1628",
booking_enabled: false,
variants: mappedVariants,
selected_variant: this.selectedVariant ? {
...this.selectedVariant,
price_formatted: this.formatPrice(this.selectedVariant.price || 0)
} : null
}
}));
},
selectVariant(v) {
if (this.selectedVariant && this.selectedVariant.id === v.id) {
this.selectedVariant = null;
this.activeImage = "https:\/\/creationhappy.smartbs.tn\/storage\/products\/hfBEvLwk-1783607474.webp";
} else {
this.selectedVariant = v;
if(v.image) this.activeImage = v.image;
}
},
selectGalleryImage(img, idx) {
this.selectedVariant = null;
this.activeImage = img;
this.galleryIndex = idx;
},
stepGallery(direction) {
if (!this.galleryImages.length) return;
this.selectedVariant = null;
this.galleryIndex = (this.galleryIndex + direction + this.galleryImages.length) % this.galleryImages.length;
this.activeImage = this.galleryImages[this.galleryIndex];
},
openQuantityPopup() {
if (this.variants.length > 0 && !this.selectedVariant) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.choose_option,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#f59e0b', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.choose_option); }
return;
}
if (this.purchaseBlocked && !this.isQuoteMode) {
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.trans.out_of_stock,
duration: 2500, gravity: 'bottom', position: 'center',
style: { background: '#ef4444', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
} else { alert(this.trans.out_of_stock); }
return;
}
this.qty = 1;
this.openQty = true;
},
toggleWishlist() {
this.inWishlist = !this.inWishlist;
if (!this.isAuth) {
let storageKey = 'smartbs_wishlist_' + this.tenantId;
let wl = JSON.parse(localStorage.getItem(storageKey) || '[]');
if (this.inWishlist) {
if (!wl.includes(this.id)) wl.push(this.id);
} else {
wl = wl.filter(id => id !== this.id);
}
localStorage.setItem(storageKey, JSON.stringify(wl));
window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { count: wl.length, product_id: this.id, added: this.inWishlist } }));
} else {
const url = 'https://creationhappy.smartbs.tn/wishlist/toggle';
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content, 'Accept': 'application/json' },
body: JSON.stringify({ product_id: this.id })
}).then(res => res.json())
.then(data => { window.dispatchEvent(new CustomEvent('wishlist-updated', { detail: { added: this.inWishlist, product_id: this.id } })); })
.catch(err => { this.inWishlist = !this.inWishlist; });
}
if (typeof Toastify !== 'undefined') {
Toastify({
text: this.inWishlist ? this.trans.added_fav : this.trans.removed_fav,
duration: 2000, gravity: 'bottom', position: 'center',
style: { background: this.inWishlist ? '#f43f5e' : '#64748b', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' }
}).showToast();
}
},
addToCart() {
this.openQty = false;
let variantId = this.selectedVariant ? this.selectedVariant.id : null;
let variantSku = this.selectedVariant ? this.selectedVariant.sku : null;
let variantName = this.selectedVariant ? this.selectedVariant.name : null;
let payload = { quantity: this.qty, variant_id: variantId, variant_sku: variantSku, variant_name: variantName };
const addUrl = 'https://creationhappy.smartbs.tn/panier/ajouter/' + this.id;
const csrfToken = document.querySelector('meta[name=\'csrf-token\']')?.content;
fetch(addUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
body: JSON.stringify(payload)
}).then(res => res.json()).then(data => {
const count = data.cart_count ?? data.count ?? '+1';
const total = data.cart_total_formatted ?? data.total_formatted ?? null;
document.querySelectorAll('.cart-count-badge').forEach(el => el.innerText = count);
if (total) document.querySelectorAll('.cart-total-price').forEach(el => el.innerText = total);
window.dispatchEvent(new Event('cart-updated'));
if (typeof window.smartTrack === 'function') {
window.smartTrack('add_to_cart', {
entity_type: this.isService ? 'service' : 'product',
item_id: this.id,
item_name: "SAC A DOS REF 1628",
quantity: this.qty
});
}
let successMsg = '';
if(this.isQuoteMode) {
successMsg = variantName ? this.trans.opt_add_quote.replace(':name', variantName) : this.trans.prod_add_quote;
} else {
successMsg = variantName ? this.trans.opt_add.replace(':name', variantName) : this.trans.prod_add;
}
if (typeof Toastify !== 'undefined') {
Toastify({ text: '✅ ' + successMsg, duration: 2500, gravity: 'bottom', position: 'center', style: { background: '#10b981', color: '#fff', borderRadius: '10px', fontWeight: 'bold', marginBottom: '80px' } }).showToast();
} else { alert(successMsg + ' !'); }
}).catch(error => { console.error('Erreur:', error); });
}
}"
@mouseenter="isHovered = true" @mouseleave="isHovered = false"
class="group relative bg-white rounded-[2rem] p-3 md:p-4 border border-slate-100 hover:border-slate-200 transition-all duration-500 hover:shadow-2xl hover:shadow-slate-200/50 flex flex-col h-full"
style="--primary-color: #e4ac07; --secondary-color: #0f172a;">
En Rupture
Sur Devis
0,000
TND
/ pièce
Aucun résultat trouvé
Nous ne trouvons aucun article correspondant à vos critères actuels.