Przejdź do głównej treści

Odwiedź nasze salony | Wrocław: pl. Strzelecki 25 | Gdynia: ul. Orłowska 66

Darmowa już od 199zł ZOBACZ

Dostawa już od 199zł ZOBACZ
Produkty w koszyku: 0. Zobacz szczegóły
Twój koszyk jest pusty
język/waluta
PL / PLN
  • 5eef58006807d3d7277b207f40fd0f17
  • 88c0df2fcf897e1e2b434271a117e7ac
  • cc9a1602d068b8df60e9dce20f972512
  • 9da2c5c3e21b7fc1e1b34d1fabc469ce
  • e101a06c4da9bd4f7e38771ccdc8dc7e
  • f3cd961e8958765faeb55bfcf249fe1f

Casamance 73819976 Le Lin 3 Shinok Grenat - tapeta len

Czas wysyłki: 6 - 8 dni roboczych
Klej gratis
Casamance
Przejdź do sekcji Opinie
Cena 659,00 zł
Cena za 1m²: 93,67 zł
rolka

Płać nawet 30 dni po zakupie

const TapProductSample = { settings: { selectors: { root: '.js__tap-product-sample', button: '.js__tap-product-sample-button', nativeBuyButton: 'buy-button', basketProduct: '.basket-product', basketStepper: 'h-input-stepper', basketStepperButton: 'h-button-stepper', basketQuantityInput: '.input-stepper__value_input' }, classes: { loading: 'tap-product-sample__button_is-loading', modalActive: 'tap-product-sample-modal_is-active' }, storefront: null, basketUpdaterApi: null, basketProductsApi: null, basketProducts: [], basketWatcherBound: false, basketApisPromise: null, basketDomObserver: null, basketDomObserverQueued: false, basketInteractionGuardBound: false, protectionStyleInjected: false }, init: (storefront) => { TapProductSample.settings.storefront = storefront; TapProductSample.injectProtectionStyles(); TapProductSample.bindBasketInteractionGuard(); TapProductSample.ensureBasketDomObserver(); TapProductSample.load(); // Prewarmujemy koszyk tylko tam, gdzie moduł może go potrzebować. // Rejestracja dynamiczna zachowuje możliwość pierwszego dodania próbki // również wtedy, gdy użytkownik nie ma jeszcze sesji koszyka. if (TapProductSample.shouldPrepareBasketApis()) { TapProductSample.ensureBasketApis().catch((error) => { console.warn('[TapProductSample] Basket prewarm failed.', error); }); } if (storefront.eventBus && typeof storefront.eventBus.on === 'function') { storefront.eventBus.on('basket.initialized', () => { TapProductSample.ensureBasketApis().catch((error) => { console.warn( '[TapProductSample] Basket API resolve after basket.initialized failed.', error ); }); TapProductSample.scheduleBasketDomProtection(); }); } }, shouldPrepareBasketApis: () => { return Boolean( document.querySelector(TapProductSample.settings.selectors.root) || window.location.pathname.includes('/basket') ); }, ensureBasketApis: async () => { if ( TapProductSample.settings.basketUpdaterApi && TapProductSample.settings.basketProductsApi ) { return true; } if (TapProductSample.settings.basketApisPromise) { return TapProductSample.settings.basketApisPromise; } TapProductSample.settings.basketApisPromise = (async () => { const storefront = TapProductSample.settings.storefront; if (!storefront) { throw new Error('Storefront API is not available.'); } let basketUpdaterApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketUpdaterApi') : null; let basketProductsApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketProductsApi') : null; // Gdy nie ma jeszcze sesji koszyka, feature "basket" może nie być // zarejestrowany. Rejestrujemy go PRZED próbą pobrania API. if (!basketUpdaterApi || !basketProductsApi) { const featureSystemApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('FeatureSystemApi') : null; if ( !featureSystemApi || typeof featureSystemApi.registerDynamic !== 'function' ) { throw new Error('FeatureSystemApi is not available.'); } try { await featureSystemApi.registerDynamic('basket', true); } catch (error) { // Inny moduł może zarejestrować basket w tym samym momencie. // Błąd rejestracji ignorujemy i od razu ponawiamy pobranie API. } basketUpdaterApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketUpdaterApi') : null; basketProductsApi = typeof storefront.getApiSync === 'function' ? storefront.getApiSync('basketProductsApi') : null; } if (!basketUpdaterApi && typeof storefront.getApi === 'function') { try { basketUpdaterApi = await storefront.getApi('basketUpdaterApi'); } catch (error) { console.warn( '[TapProductSample] basketUpdaterApi getApi() failed.', error ); } } if (!basketProductsApi && typeof storefront.getApi === 'function') { try { basketProductsApi = await storefront.getApi('basketProductsApi'); } catch (error) { console.warn( '[TapProductSample] basketProductsApi getApi() failed.', error ); } } if (!basketUpdaterApi || !basketProductsApi) { throw new Error('Basket APIs could not be initialized.'); } TapProductSample.settings.basketUpdaterApi = basketUpdaterApi; TapProductSample.settings.basketProductsApi = basketProductsApi; TapProductSample.watchBasket(); return true; })(); try { return await TapProductSample.settings.basketApisPromise; } catch (error) { TapProductSample.settings.basketApisPromise = null; throw error; } }, waitForBasket: async () => { return TapProductSample.ensureBasketApis(); }, load: () => { TapProductSample.injectProtectionStyles(); TapProductSample.bindBasketInteractionGuard(); TapProductSample.ensureBasketDomObserver(); document .querySelectorAll(TapProductSample.settings.selectors.root) .forEach((root) => { if (root.dataset.tapProductSampleReady !== '1') { root.dataset.tapProductSampleReady = '1'; const button = root.querySelector( TapProductSample.settings.selectors.button ); if (button) { button.addEventListener('click', () => { TapProductSample.handleClick(root, button); }); } } TapProductSample.bindNativeSampleProductGuard(root); TapProductSample.protectNativeSampleProductQuantity(root); }); TapProductSample.scheduleBasketDomProtection(); }, injectProtectionStyles: () => { if (TapProductSample.settings.protectionStyleInjected) { return; } const style = document.createElement('style'); style.id = 'tap-product-sample-protection-styles'; style.textContent = ` .tap-product-sample-basket-item h-input-stepper h-button-stepper[type="increment"] { display: none !important; } .tap-product-sample-basket-item_quantity-valid h-input-stepper h-button-stepper[type="decrement"] { display: none !important; } .tap-product-sample-basket-item .input-stepper__value_input, .tap-product-sample-native-quantity-locked .input-stepper__value_input { pointer-events: none !important; user-select: none !important; caret-color: transparent !important; } .tap-product-sample-native-quantity-locked h-button-stepper { display: none !important; } `; document.head.appendChild(style); TapProductSample.settings.protectionStyleInjected = true; }, ensureBasketDomObserver: () => { if ( TapProductSample.settings.basketDomObserver || !document.body || typeof MutationObserver !== 'function' ) { return; } TapProductSample.settings.basketDomObserver = new MutationObserver( (mutations) => { const shouldProtect = mutations.some((mutation) => { if (mutation.type === 'childList') { return mutation.addedNodes && mutation.addedNodes.length > 0; } if (mutation.type === 'attributes') { return Boolean( mutation.target.closest?.( TapProductSample.settings.selectors.basketProduct ) ); } return false; }); if (shouldProtect) { TapProductSample.scheduleBasketDomProtection(); } } ); TapProductSample.settings.basketDomObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['value'] }); }, scheduleBasketDomProtection: () => { if (TapProductSample.settings.basketDomObserverQueued) { return; } TapProductSample.settings.basketDomObserverQueued = true; window.requestAnimationFrame(() => { TapProductSample.settings.basketDomObserverQueued = false; TapProductSample.protectBasketDom(); }); }, bindBasketInteractionGuard: () => { if (TapProductSample.settings.basketInteractionGuardBound) { return; } TapProductSample.settings.basketInteractionGuardBound = true; document.addEventListener( 'click', (event) => { const stepperButton = event.target.closest( TapProductSample.settings.selectors.basketStepperButton ); if (!stepperButton) { return; } const basketProduct = stepperButton.closest( TapProductSample.settings.selectors.basketProduct ); if ( !basketProduct || !TapProductSample.isBasketProductElementSample(basketProduct) ) { return; } const buttonType = stepperButton.getAttribute('type'); const quantity = TapProductSample.getBasketProductDomQuantity( basketProduct ); // Jeżeli przed wdrożeniem próbka miała już błędnie ilość > 1, // pozwalamy tylko ją zmniejszyć do 1. Zwiększanie jest zawsze blokowane. if (buttonType === 'decrement' && quantity > 1) { return; } event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); }, true ); document.addEventListener( 'keydown', (event) => { const input = event.target.closest( TapProductSample.settings.selectors.basketQuantityInput ); if (!input) { return; } const basketProduct = input.closest( TapProductSample.settings.selectors.basketProduct ); if ( !basketProduct || !TapProductSample.isBasketProductElementSample(basketProduct) ) { return; } event.preventDefault(); event.stopPropagation(); }, true ); document.addEventListener( 'beforeinput', (event) => { const input = event.target.closest( TapProductSample.settings.selectors.basketQuantityInput ); if (!input) { return; } const basketProduct = input.closest( TapProductSample.settings.selectors.basketProduct ); if ( !basketProduct || !TapProductSample.isBasketProductElementSample(basketProduct) ) { return; } event.preventDefault(); event.stopPropagation(); }, true ); }, watchBasket: () => { const basketProductsApi = TapProductSample.settings.basketProductsApi; if (!basketProductsApi || TapProductSample.settings.basketWatcherBound) { return; } TapProductSample.settings.basketWatcherBound = true; if (typeof basketProductsApi.get === 'function') { try { const products = basketProductsApi.get(); const items = TapProductSample.getBasketItemsFromBasketObject(products); if (items.length || Array.isArray(products)) { TapProductSample.settings.basketProducts = items; } } catch (error) { console.warn( '[TapProductSample] Initial basketProductsApi.get() failed.', error ); } } if (typeof basketProductsApi.select$ !== 'function') { return; } const basketProducts$ = basketProductsApi.select$(); if (basketProducts$ && typeof basketProducts$.subscribe === 'function') { basketProducts$.subscribe((products) => { TapProductSample.settings.basketProducts = TapProductSample.getBasketItemsFromBasketObject(products); TapProductSample.scheduleBasketDomProtection(); }); } }, splitIds: (value) => { return String(value || '') .split(/[\s,;]+/) .map((item) => item.trim()) .filter(Boolean); }, normalizeId: (value) => { return String(value || '').trim(); }, normalizeText: (value) => { return String(value || '') .trim() .toLocaleLowerCase('pl-PL') .normalize('NFD') .replace(/[\u0300-\u036f]/g, ''); }, getKnownSampleProductIdsFromDom: () => { const ids = new Set(); document .querySelectorAll(TapProductSample.settings.selectors.root) .forEach((root) => { TapProductSample.splitIds(root.dataset.allSampleProductIds).forEach( (id) => ids.add(id) ); TapProductSample.getSampleCategoryProductIds(root).forEach( (id) => ids.add(id) ); }); return Array.from(ids); }, getBasketProductOriginalProductId: (basketProduct) => { const optionNodes = basketProduct.querySelectorAll( '.basket-product__options [data-option-name]' ); for (const optionNode of optionNodes) { if ( TapProductSample.normalizeText(optionNode.dataset.optionName) === 'id produktu' ) { return TapProductSample.normalizeId(optionNode.dataset.optionValue); } } return ''; }, getBasketProductDisplayName: (basketProduct) => { const link = basketProduct.querySelector( '.basket-product__link, .basket-product__name a, .basket-product__name' ); return TapProductSample.normalizeText(link?.textContent || ''); }, isBasketProductElementSample: (basketProduct) => { if (!basketProduct) { return false; } // Próbki dodawane przez ten moduł mają w koszyku opcję "ID produktu". if (TapProductSample.getBasketProductOriginalProductId(basketProduct)) { return true; } const productId = TapProductSample.normalizeId( basketProduct.dataset.productId ); if ( productId && TapProductSample.getKnownSampleProductIdsFromDom().includes(productId) ) { return true; } // Na stronie koszyka skrypt modułu jest ładowany globalnie, ale nie ma tam // jego Twiga z konfiguracją. Produkty z kategorii /probniki rozpoznajemy // dodatkowo po ich nazwach ("Próbka...", "Zestaw próbek..."). const name = TapProductSample.getBasketProductDisplayName(basketProduct); return ( name.includes('probka') || name.includes('probek') || name.includes('probki') ); }, getBasketProductDomQuantity: (basketProduct) => { const stepper = basketProduct.querySelector( TapProductSample.settings.selectors.basketStepper ); const value = stepper?.getAttribute('value') || '1'; const quantity = parseInt(value, 10); return Number.isFinite(quantity) && quantity > 0 ? quantity : 1; }, countSamplesInDom: () => { return Array.from( document.querySelectorAll( TapProductSample.settings.selectors.basketProduct ) ).reduce((sum, basketProduct) => { if (!TapProductSample.isBasketProductElementSample(basketProduct)) { return sum; } return sum + TapProductSample.getBasketProductDomQuantity(basketProduct); }, 0); }, hasDuplicateSampleInDom: (root) => { const productId = TapProductSample.normalizeId(root?.dataset?.productId); if (!productId) { return false; } return Array.from( document.querySelectorAll( TapProductSample.settings.selectors.basketProduct ) ).some( (basketProduct) => TapProductSample.getBasketProductOriginalProductId(basketProduct) === productId ); }, hasNativeSampleProductInDom: (root) => { const productId = TapProductSample.normalizeId(root?.dataset?.productId); if (!productId) { return false; } return Array.from( document.querySelectorAll( TapProductSample.settings.selectors.basketProduct ) ).some( (basketProduct) => TapProductSample.normalizeId(basketProduct.dataset.productId) === productId ); }, lockBasketProductQuantity: (basketProduct) => { if (!basketProduct) { return; } basketProduct.classList.add('tap-product-sample-basket-item'); const stepper = basketProduct.querySelector( TapProductSample.settings.selectors.basketStepper ); if (!stepper) { return; } const quantity = TapProductSample.getBasketProductDomQuantity(basketProduct); const isValidQuantity = quantity <= 1; basketProduct.classList.toggle( 'tap-product-sample-basket-item_quantity-valid', isValidQuantity ); stepper.setAttribute('min', '1'); if (isValidQuantity) { stepper.setAttribute('max', '1'); } stepper .querySelectorAll(TapProductSample.settings.selectors.basketStepperButton) .forEach((button) => { const buttonType = button.getAttribute('type'); const canRepairQuantity = buttonType === 'decrement' && quantity > 1; if (canRepairQuantity) { button.removeAttribute('aria-disabled'); button.setAttribute('tabindex', '0'); return; } button.setAttribute('aria-disabled', 'true'); button.setAttribute('tabindex', '-1'); }); const input = stepper.querySelector( TapProductSample.settings.selectors.basketQuantityInput ); if (input) { input.readOnly = true; input.setAttribute('aria-readonly', 'true'); input.setAttribute('tabindex', '-1'); } }, protectBasketDom: () => { document .querySelectorAll(TapProductSample.settings.selectors.basketProduct) .forEach((basketProduct) => { if (TapProductSample.isBasketProductElementSample(basketProduct)) { TapProductSample.lockBasketProductQuantity(basketProduct); } }); }, protectNativeSampleProductQuantity: (root) => { if (root?.dataset?.isSampleCategoryProduct !== '1') { return; } const productId = TapProductSample.normalizeId(root.dataset.productId); if (!productId) { return; } document .querySelectorAll(`product-quantity[product-id="${productId}"]`) .forEach((productQuantity) => { productQuantity.classList.add( 'tap-product-sample-native-quantity-locked' ); productQuantity.setAttribute('value', '1'); const stepper = productQuantity.querySelector( TapProductSample.settings.selectors.basketStepper ); if (stepper) { stepper.setAttribute('min', '1'); stepper.setAttribute('max', '1'); stepper.setAttribute('value', '1'); stepper .querySelectorAll( TapProductSample.settings.selectors.basketStepperButton ) .forEach((button) => { button.setAttribute('aria-disabled', 'true'); button.setAttribute('tabindex', '-1'); }); const input = stepper.querySelector( TapProductSample.settings.selectors.basketQuantityInput ); if (input) { input.readOnly = true; input.setAttribute('aria-readonly', 'true'); input.setAttribute('tabindex', '-1'); } } }); document .querySelectorAll(`buy-button[product-id="${productId}"]`) .forEach((buyButton) => { buyButton.setAttribute('quantity', '1'); }); }, getSampleLimit: (root) => { const limit = parseInt(root?.dataset?.sampleLimit || '5', 10); return Number.isFinite(limit) && limit > 0 ? limit : 5; }, getSampleCategoryProductIds: (root) => { return TapProductSample.splitIds(root?.dataset?.sampleCategoryProductIds); }, getNativeBuyButtonQuantity: (buyButton) => { const quantity = parseInt(buyButton?.getAttribute('quantity') || '1', 10); return Number.isFinite(quantity) && quantity > 0 ? quantity : 1; }, getQuantity: (item) => { const quantity = item?.quantity ?? item?.count ?? item?.amount ?? item?.qty ?? 1; const parsed = parseInt(quantity, 10); return Number.isFinite(parsed) && parsed > 0 ? parsed : 1; }, getItemProductId: (item) => { return TapProductSample.normalizeId( item?.productId ?? item?.product_id ?? item?.product?.id ?? item?.product?.productId ?? item?.id ?? '' ); }, getItemVariantId: (item) => { return TapProductSample.normalizeId( item?.variantId ?? item?.variant_id ?? item?.stockId ?? item?.stock_id ?? item?.variant?.id ?? item?.stock?.id ?? '' ); }, collectStrings: (value, output = [], depth = 0) => { if (depth > 6 || value === null || value === undefined) { return output; } if (typeof value === 'string' || typeof value === 'number') { output.push(String(value)); return output; } if (Array.isArray(value)) { value.forEach((item) => { TapProductSample.collectStrings(item, output, depth + 1); }); return output; } if (typeof value === 'object') { Object.keys(value).forEach((key) => { output.push(String(key)); TapProductSample.collectStrings(value[key], output, depth + 1); }); } return output; }, getBasketItemsFromBasketObject: (basket) => { if (!basket) { return []; } if (Array.isArray(basket)) { return basket; } const candidates = [ basket.products?.list, basket.products, basket.items, basket.lines, basket.positions, basket.basket?.products?.list, basket.basket?.products, basket.basket?.items, basket.basket?.lines, basket.data?.products?.list, basket.data?.products, basket.data?.items ]; for (const candidate of candidates) { if (Array.isArray(candidate)) { return candidate; } } return []; }, getBasketItems: async () => { await TapProductSample.waitForBasket(); const basketProductsApi = TapProductSample.settings.basketProductsApi; const basketUpdaterApi = TapProductSample.settings.basketUpdaterApi; // Przy każdym sprawdzeniu limitu próbujemy pobrać najświeższy stan koszyka. // Poprzednio cache z subskrypcji mógł być jeszcze nieaktualny po szybkich zmianach. if (basketUpdaterApi && typeof basketUpdaterApi.getCurrentBasket === 'function') { try { const basket = await basketUpdaterApi.getCurrentBasket(); const items = TapProductSample.getBasketItemsFromBasketObject(basket); TapProductSample.settings.basketProducts = items; return items; } catch (error) {} } if (basketProductsApi && typeof basketProductsApi.get === 'function') { try { const products = basketProductsApi.get(); const items = TapProductSample.getBasketItemsFromBasketObject(products); if (items.length || Array.isArray(products)) { TapProductSample.settings.basketProducts = items; return items; } } catch (error) {} } return TapProductSample.settings.basketProducts; }, isSampleItem: ( item, sampleProductIds, sampleCategoryProductIds, sampleProductId, sampleVariantId ) => { const itemProductId = TapProductSample.getItemProductId(item); const itemVariantId = TapProductSample.getItemVariantId(item); const strings = TapProductSample.collectStrings(item); if (itemProductId && sampleProductIds.includes(itemProductId)) { return true; } if (itemProductId && sampleCategoryProductIds.includes(itemProductId)) { return true; } if (sampleProductId && itemProductId === sampleProductId) { return true; } if (sampleVariantId && itemVariantId === sampleVariantId) { return true; } if (sampleProductIds.some((id) => strings.includes(id))) { return true; } return sampleCategoryProductIds.some((id) => strings.includes(id)); }, hasDuplicateSample: (items, root) => { const productId = TapProductSample.normalizeId(root.dataset.productId); const sampleProductId = TapProductSample.normalizeId(root.dataset.sampleProductId); const sampleVariantId = TapProductSample.normalizeId(root.dataset.sampleVariantId); const sampleProductIds = TapProductSample.splitIds(root.dataset.allSampleProductIds); const sampleCategoryProductIds = TapProductSample.getSampleCategoryProductIds(root); return items.some((item) => { if ( !TapProductSample.isSampleItem( item, sampleProductIds, sampleCategoryProductIds, sampleProductId, sampleVariantId ) ) { return false; } const strings = TapProductSample.collectStrings(item); return productId && strings.includes(productId); }); }, countSamples: (items, root) => { const sampleProductId = TapProductSample.normalizeId(root.dataset.sampleProductId); const sampleVariantId = TapProductSample.normalizeId(root.dataset.sampleVariantId); const sampleProductIds = TapProductSample.splitIds(root.dataset.allSampleProductIds); const sampleCategoryProductIds = TapProductSample.getSampleCategoryProductIds(root); return items.reduce((sum, item) => { if ( TapProductSample.isSampleItem( item, sampleProductIds, sampleCategoryProductIds, sampleProductId, sampleVariantId ) ) { return sum + TapProductSample.getQuantity(item); } return sum; }, 0); }, hasNativeSampleProductDuplicate: (items, root) => { const productId = TapProductSample.normalizeId(root?.dataset?.productId); if (!productId) { return false; } return items.some((item) => { if (TapProductSample.getItemProductId(item) === productId) { return true; } return TapProductSample.collectStrings(item).includes(productId); }); }, bindNativeSampleProductGuard: (root) => { if (root.dataset.isSampleCategoryProduct !== '1') { return; } const currentProductId = TapProductSample.normalizeId(root.dataset.productId); document .querySelectorAll(TapProductSample.settings.selectors.nativeBuyButton) .forEach((buyButton) => { if ( TapProductSample.normalizeId(buyButton.getAttribute('product-id')) !== currentProductId || buyButton.dataset.tapProductSampleGuardReady === '1' ) { return; } buyButton.dataset.tapProductSampleGuardReady = '1'; buyButton.setAttribute('quantity', '1'); buyButton.addEventListener( 'click', async (event) => { if (buyButton.dataset.tapProductSampleGuardBypass === '1') { delete buyButton.dataset.tapProductSampleGuardBypass; return; } const nativeButton = event.target.closest('button'); if (!nativeButton || !buyButton.contains(nativeButton)) { return; } event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); try { const items = await TapProductSample.getBasketItems(); const sampleLimit = TapProductSample.getSampleLimit(root); const apiSamplesCount = TapProductSample.countSamples(items, root); const domSamplesCount = TapProductSample.countSamplesInDom(); const currentSamplesCount = Math.max( apiSamplesCount, domSamplesCount ); if ( TapProductSample.hasNativeSampleProductDuplicate(items, root) || TapProductSample.hasNativeSampleProductInDom(root) ) { TapProductSample.showModal( root.dataset.duplicateMessage || 'Już dodałeś próbkę tego produktu' ); return; } if (currentSamplesCount + 1 > sampleLimit) { TapProductSample.showModal( root.dataset.limitMessage || 'Możesz zamówić maksymalnie 5 próbek!' ); return; } TapProductSample.protectNativeSampleProductQuantity(root); buyButton.setAttribute('quantity', '1'); buyButton.dataset.tapProductSampleGuardBypass = '1'; nativeButton.click(); } catch (error) { console.error( '[TapProductSample] Native sample limit check failed.', error ); TapProductSample.showModal( root.dataset.errorMessage || 'Wystąpił błąd podczas dodawania próbki' ); } }, true ); }); }, createModal: () => { let modal = document.querySelector('.tap-product-sample-modal'); if (modal) { return modal; } modal = document.createElement('div'); modal.className = 'tap-product-sample-modal'; modal.innerHTML = ` `; document.body.appendChild(modal); modal.addEventListener('click', (event) => { if (event.target === modal) { TapProductSample.closeModal(); } }); modal.querySelector('.tap-product-sample-modal__close').addEventListener('click', () => { TapProductSample.closeModal(); }); document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { TapProductSample.closeModal(); } }); return modal; }, showModal: (message, type = 'info') => { const modal = TapProductSample.createModal(); const messageNode = modal.querySelector('.tap-product-sample-modal__message'); const iconNode = modal.querySelector('.tap-product-sample-modal__icon'); if (messageNode) { messageNode.textContent = message; } if (iconNode) { if (type === 'success') { iconNode.innerHTML = ` `; } else { iconNode.textContent = 'i'; } } modal.classList.add(TapProductSample.settings.classes.modalActive); }, closeModal: () => { const modal = document.querySelector('.tap-product-sample-modal'); if (modal) { modal.classList.remove(TapProductSample.settings.classes.modalActive); } }, addSampleToBasket: async (root) => { await TapProductSample.waitForBasket(); const basketUpdaterApi = TapProductSample.settings.basketUpdaterApi; if (!basketUpdaterApi || typeof basketUpdaterApi.addItem !== 'function') { throw new Error('Basket updater API is not available.'); } const variantOptions = {}; variantOptions[root.dataset.productNameOptionId] = root.dataset.productName || ''; variantOptions[root.dataset.productIdOptionId] = root.dataset.productId || ''; const item = { variantId: Number(root.dataset.sampleVariantId), quantity: 1, variantOptions, showAddedModal: false, bundleItems: [] }; const basket = await basketUpdaterApi.addItem(item); const returnedItems = TapProductSample.getBasketItemsFromBasketObject(basket); if (returnedItems.length) { TapProductSample.settings.basketProducts = returnedItems; } if (basketUpdaterApi.refreshBasket && typeof basketUpdaterApi.refreshBasket === 'function') { try { await basketUpdaterApi.refreshBasket(); } catch (error) {} } return basket; }, handleClick: async (root, button) => { if (button.classList.contains(TapProductSample.settings.classes.loading)) { return; } button.classList.add(TapProductSample.settings.classes.loading); button.disabled = true; try { const items = await TapProductSample.getBasketItems(); const sampleLimit = TapProductSample.getSampleLimit(root); const apiSamplesCount = TapProductSample.countSamples(items, root); const domSamplesCount = TapProductSample.countSamplesInDom(); const currentSamplesCount = Math.max(apiSamplesCount, domSamplesCount); if ( TapProductSample.hasDuplicateSample(items, root) || TapProductSample.hasDuplicateSampleInDom(root) ) { TapProductSample.showModal( root.dataset.duplicateMessage || 'Już dodałeś próbkę tego produktu' ); return; } if (currentSamplesCount + 1 > sampleLimit) { TapProductSample.showModal(root.dataset.limitMessage || 'Możesz zamówić maksymalnie 5 próbek!'); return; } await TapProductSample.addSampleToBasket(root); TapProductSample.scheduleBasketDomProtection(); if (root.dataset.afterAddAction === 'cart') { window.location.href = root.dataset.basketUrl || '/basket'; return; } TapProductSample.showModal( root.dataset.addedMessage || 'Próbka została dodana do koszyka', 'success' ); } catch (error) { console.error('[TapProductSample] Sample add failed.', error); TapProductSample.showModal(root.dataset.errorMessage || 'Wystąpił błąd podczas dodawania próbki'); } finally { button.classList.remove(TapProductSample.settings.classes.loading); button.disabled = false; } } }; useStorefront(async (storefront) => { TapProductSample.init(storefront); storefront.eventBus.on('PageManager.loaded', () => { TapProductSample.load(); if (TapProductSample.shouldPrepareBasketApis()) { TapProductSample.ensureBasketApis().catch((error) => { console.warn( '[TapProductSample] Basket resolve after PageManager.loaded failed.', error ); }); } }); });
Długość: 10.05 m
Szerokość: 70 cm
Pasowanie: Dowolne
Rodzaj: Tapeta winylowa na podłożu flizelinowym

Oblicz zapotrzebowanie

? Aby obliczyć zapotrzebowanie, należy podać wymiary ściany

Podaj wymiary ściany:

Potrzebujesz porady? Odpowiemy na Twoje pytania!

Gwarancja bezpiecznego zakupu

Wymiary tapet Casamance

  • Długość 10.05 m
  • Szerokość 70 cm
  • Pasowanie Dowolne
  • Rodzaj Tapeta winylowa na podłożu flizelinowym

Kolor

  • Brązowy tak
  • Srebrny nie
  • Złoty nie
Zapytaj o produkt
Udostępnij

Opis produktu

Tapeta Casamance Le Lin 3 Shinok F7381 99 76 Grenat

Tapeta Casamance Le Lin 3 Shinok 73819976 Grenat to wyrafinowane połączenie subtelności i dzikiej natury materiału. Jej design czerpie z unikalnej faktury lnu, przekształcając ściany w płótno delikatnej elegancji. Sugestie sęków, celowo uwydatnione, podkreślają surowy, naturalny charakter tapety, jednocześnie akcentując jej wymiar dotykowy. Nazwa nawiązująca do ciepłego, suchego wiatru z Gór Skalistych i zachodniej Ameryki Północnej, symbolizuje siłę i zmienność natury, co znajduje odzwierciedlenie w subtelności i bogactwie kolorystycznym tej kolekcji. Z olśniewającą paletą 55 kolorów, każdy znajdzie coś dla siebie, od spokojnych pasteli po głębokie, nasycenie barwy. Tapeta Casamance 73819976 Le Lin 3 Shinok Grenat to doskonały wybór dla osób szukających połączenia natury, dotyku i wizualnej głębi, nadając wnętrzom unikalny, osobisty charakter.

O kolekcji tapet Casamance Le Lin 3

Kolekcja tapet dekoracyjnych Casamance Le Lin 3 to kwintesencja elegancji i naturalności, skrojona na miarę współczesnych wnętrz szukających połączenia tradycji z nowoczesnością. Wykorzystując len jako bazowy materiał roślinny, kolekcja ta oddaje hołd jego autentycznej, tekstylnej prezencji, wprowadzając użytkownika w świat bogatych, naturalnych odcieni inspirowanych bezpośrednio naturą.


Zobacz wszystkie tapety Casamance

Producent: Casamance
Długość: 10.05 m
Szerokość: 70 cm
Pasowanie: Dowolne
Rodzaj: Tapeta winylowa na podłożu flizelinowym
5eef58006807d3d7277b207f40fd0f17
Casamance 73819976 Le Lin 3 Shinok Grenat - tapeta len

Inne produkty z tej kategorii