/** * PMPNZNG Shop — Client-JS (Vanilla, kein Framework) * Cart · Passwort-Gate · Toasts · Galerie · Größenwahl · Mobile-Menü · Insta · Exit-Intent * * Produktdaten kommen aus dem Astro-Layout (window.__PRODUCTS__), * damit es nur EINE Quelle der Wahrheit gibt (src/data/products.ts). * Später ersetzt Medusa sowohl die Render-Daten als auch den Checkout. */ 'use strict'; const PRODUCTS = Array.isArray(window.__PRODUCTS__) ? window.__PRODUCTS__ : []; /* ===================== PASSWORT-GATE ===================== */ const PASS_KEY = 'pmpnzng_auth'; const PASS_WORD = null; /* Passwortschutz entfernt */ function initPasswordGate() { if (sessionStorage.getItem(PASS_KEY) === '1') return; const overlay = document.createElement('div'); overlay.id = 'pw-overlay'; overlay.setAttribute('role', 'dialog'); overlay.setAttribute('aria-modal', 'true'); overlay.setAttribute('aria-label', 'Passwortschutz'); overlay.innerHTML = `
Vorschau · tgasolutions-shop.de

Diese Seite ist passwortgeschützt.
Bitte gib das Passwort ein, um fortzufahren.

`; document.body.appendChild(overlay); document.body.style.overflow = 'hidden'; setTimeout(() => document.getElementById('pw-input')?.focus(), 100); document.getElementById('pw-form').addEventListener('submit', (e) => { e.preventDefault(); const val = document.getElementById('pw-input').value; if (val === PASS_WORD) { sessionStorage.setItem(PASS_KEY, '1'); overlay.style.transition = 'opacity 0.4s ease'; overlay.style.opacity = '0'; setTimeout(() => { overlay.remove(); document.body.style.overflow = ''; }, 400); } else { document.getElementById('pw-error').textContent = 'Falsches Passwort. Bitte versuche es erneut.'; const input = document.getElementById('pw-input'); input.value = ''; input.focus(); const box = overlay.querySelector('.pw-box'); box.classList.add('pw-shake'); setTimeout(() => box.classList.remove('pw-shake'), 500); } }); } /* ===================== CART ===================== */ const CART_KEY = 'pmpnzng_cart'; const getCart = () => { try { return JSON.parse(localStorage.getItem(CART_KEY)) || []; } catch { return []; } }; const saveCart = (cart) => { localStorage.setItem(CART_KEY, JSON.stringify(cart)); updateCartUI(); }; function addToCart(productId, size, qty = 1) { const cart = getCart(); const product = PRODUCTS.find((p) => p.id === productId); if (!product) return; const key = `${productId}_${size}`; const existing = cart.find((i) => i.key === key); if (existing) existing.qty += qty; else cart.push({ key, productId, size, qty, name: product.name, price: product.price, image: product.cardImage }); saveCart(cart); showToast(`„${product.shortName}" wurde in den Warenkorb gelegt.`); } const removeFromCart = (key) => saveCart(getCart().filter((i) => i.key !== key)); function updateQty(key, delta) { const cart = getCart(); const item = cart.find((i) => i.key === key); if (item) { item.qty = Math.max(1, item.qty + delta); saveCart(cart); } } const cartTotal = () => getCart().reduce((s, i) => s + i.price * i.qty, 0); const cartCount = () => getCart().reduce((s, i) => s + i.qty, 0); function updateCartUI() { const count = cartCount(); document.querySelectorAll('.js-cart-count').forEach((el) => { el.textContent = count; el.style.display = count > 0 ? 'inline-flex' : 'none'; }); document.querySelectorAll('.js-cart-total').forEach((el) => { el.textContent = formatPrice(cartTotal()); }); } /* ===================== TOASTS ===================== */ function showToast(message, type = 'success', duration = 3500) { let c = document.getElementById('toast-container'); if (!c) { c = document.createElement('div'); c.id = 'toast-container'; c.setAttribute('aria-live', 'polite'); document.body.appendChild(c); } const t = document.createElement('div'); t.className = `toast toast-${type}`; t.setAttribute('role', 'status'); t.innerHTML = `${type === 'success' ? '✓' : '!'}${message}`; c.appendChild(t); requestAnimationFrame(() => requestAnimationFrame(() => t.classList.add('toast-visible'))); setTimeout(() => { t.classList.remove('toast-visible'); setTimeout(() => t.remove(), 350); }, duration); } /* ===================== GALERIE ===================== */ function initGallery() { const main = document.getElementById('gallery-main'); const thumbs = document.querySelectorAll('.gallery-thumb'); if (!main || !thumbs.length) return; thumbs.forEach((thumb, i) => { thumb.addEventListener('click', () => { main.src = thumb.dataset.src || thumb.src; main.alt = thumb.alt; thumbs.forEach((t) => t.classList.remove('active')); thumb.classList.add('active'); }); if (i === 0) thumb.classList.add('active'); }); } /* ===================== GRÖSSENWAHL ===================== */ function initSizeSelector() { const btns = document.querySelectorAll('.size-btn'); const input = document.getElementById('selected-size'); const addBtn = document.getElementById('add-to-cart-btn'); if (!btns.length) return; btns.forEach((btn) => { btn.addEventListener('click', () => { btns.forEach((b) => b.classList.remove('active')); btn.classList.add('active'); if (input) input.value = btn.dataset.size; if (addBtn) { addBtn.disabled = false; addBtn.textContent = 'In den Warenkorb'; } }); }); } /* ===================== ADD TO CART ===================== */ function initAddToCart() { const form = document.getElementById('product-form'); if (form) { form.addEventListener('submit', (e) => { e.preventDefault(); const productId = form.dataset.productId; const sizeInput = document.getElementById('selected-size'); const size = sizeInput ? sizeInput.value : 'One Size'; if (!size) { showToast('Bitte wähle zuerst eine Größe.', 'warning'); return; } addToCart(productId, size); }); } document.querySelectorAll('.js-quick-add').forEach((btn) => { btn.addEventListener('click', (e) => { e.preventDefault(); addToCart(btn.dataset.productId, btn.dataset.size || 'One Size'); }); }); } /* ===================== WARENKORB-SEITE ===================== */ function renderCartPage() { const container = document.getElementById('cart-items'); const emptyMsg = document.getElementById('cart-empty'); const summary = document.getElementById('cart-summary'); if (!container) return; const cart = getCart(); if (cart.length === 0) { container.innerHTML = ''; if (emptyMsg) emptyMsg.style.display = 'block'; if (summary) summary.style.display = 'none'; return; } if (emptyMsg) emptyMsg.style.display = 'none'; if (summary) summary.style.display = 'block'; container.innerHTML = cart.map((item) => `
${item.name}

${item.name}

Größe: ${item.size}
${item.qty}
${formatPrice(item.price * item.qty)}
`).join(''); container.querySelectorAll('.qty-btn').forEach((btn) => btn.addEventListener('click', () => { updateQty(btn.dataset.key, parseInt(btn.dataset.delta)); renderCartPage(); })); container.querySelectorAll('.cart-remove').forEach((btn) => btn.addEventListener('click', () => { removeFromCart(btn.dataset.key); renderCartPage(); showToast('Artikel entfernt.', 'info'); })); document.querySelectorAll('.js-cart-subtotal').forEach((el) => { el.textContent = formatPrice(cartTotal()); }); } /* ===================== CHECKOUT (simuliert — später Stripe via Medusa) ===================== */ function initCheckout() { const form = document.getElementById('checkout-form'); if (!form) return; const summaryContainer = document.getElementById('checkout-order-items'); if (summaryContainer) { const cart = getCart(); summaryContainer.innerHTML = cart.map((i) => `
${i.name} (${i.size}) × ${i.qty}${formatPrice(i.price * i.qty)}
`).join(''); document.querySelectorAll('.js-checkout-total').forEach((el) => { el.textContent = formatPrice(cartTotal() + (cartTotal() >= 50 ? 0 : 4.9)); }); document.querySelectorAll('.js-checkout-shipping').forEach((el) => { el.textContent = cartTotal() >= 50 ? 'Kostenlos' : formatPrice(4.9); }); } form.addEventListener('submit', async (e) => { e.preventDefault(); const cart = getCart(); if (!cart.length) { showToast('Dein Warenkorb ist leer.', 'warning'); return; } const fd = new FormData(form); const btn = form.querySelector('.checkout-submit-btn'); if (btn) { btn.disabled = true; btn.textContent = 'Wird verarbeitet …'; } try { const res = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: cart, contact: Object.fromEntries(fd.entries()) }), }); const data = await res.json(); if (data.url) { localStorage.removeItem(CART_KEY); window.location.href = data.url; return; } throw new Error(data.error || 'Fehler'); } catch (err) { showToast('Checkout fehlgeschlagen. Bitte erneut versuchen.', 'warning'); if (btn) { btn.disabled = false; btn.textContent = 'Jetzt bestellen →'; } } }); } /* ===================== INSTAGRAM-FEED ===================== */ const INSTA_POSTS = [ { img: '/product-images/insta/insta-2.jpg', url: 'https://www.instagram.com/tgasolutions/reel/DVMOZMGiDto/' }, { img: '/product-images/insta/insta-3.jpg', url: 'https://www.instagram.com/tgasolutions/reel/DVDX5KQiB8Z/' }, { img: '/product-images/insta/insta-4.jpg', url: 'https://www.instagram.com/tgasolutions/reel/DU_br0OCE35/' }, { img: '/product-images/insta/insta-1.jpg', url: 'https://www.instagram.com/tgasolutions/reel/DVgqeRQiNnD/' }, { img: '/product-images/insta/insta-5.jpg', url: 'https://www.instagram.com/tgasolutions/reel/DVVazsxiIB8/' }, { img: '/product-images/insta/insta-6.jpg', url: 'https://www.instagram.com/tgasolutions/reel/DVDX5KQiB8Z/' }, ]; function initInstaFeed() { const grid = document.getElementById('insta-grid'); if (!grid || !INSTA_POSTS.length) return; grid.innerHTML = ''; grid.className = 'insta-grid'; grid.setAttribute('role', 'list'); INSTA_POSTS.forEach((post) => { const tile = document.createElement('a'); tile.className = 'insta-post'; tile.href = post.url; tile.target = '_blank'; tile.rel = 'noopener noreferrer'; tile.setAttribute('role', 'listitem'); tile.setAttribute('aria-label', '@tgasolutions auf Instagram'); tile.innerHTML = `@tgasolutions
@tgasolutions
`; grid.appendChild(tile); }); } /* ===================== MOBILE-MENÜ ===================== */ function initMobileMenu() { const toggle = document.getElementById('menu-toggle'); const nav = document.getElementById('main-nav'); if (!toggle || !nav) return; toggle.addEventListener('click', () => { const open = nav.classList.toggle('nav-open'); toggle.setAttribute('aria-expanded', open); document.body.style.overflow = open ? 'hidden' : ''; }); nav.querySelectorAll('a').forEach((a) => a.addEventListener('click', () => { nav.classList.remove('nav-open'); toggle.setAttribute('aria-expanded', 'false'); document.body.style.overflow = ''; })); } /* ===================== STICKY MOBILE ADD-TO-CART ===================== */ function initStickyMobileATC() { const mainBtn = document.getElementById('add-to-cart-btn'); if (!mainBtn || window.innerWidth > 900) return; const nameEl = document.querySelector('[class*="pdp-name"]') || document.querySelector('h1'); const productName = nameEl ? nameEl.textContent.trim().split('\n')[0].trim() : 'Produkt'; const bar = document.createElement('div'); bar.id = 'sticky-atc'; bar.setAttribute('aria-hidden', 'true'); bar.innerHTML = `${productName.substring(0, 40)}`; document.body.appendChild(bar); const observer = new IntersectionObserver(([entry]) => { bar.classList.toggle('sticky-atc-visible', !entry.isIntersecting); bar.setAttribute('aria-hidden', entry.isIntersecting ? 'true' : 'false'); }, { threshold: 0, rootMargin: '0px 0px -60px 0px' }); observer.observe(mainBtn); document.getElementById('sticky-atc-btn').addEventListener('click', () => { if (!mainBtn.disabled) mainBtn.click(); else { mainBtn.scrollIntoView({ behavior: 'smooth', block: 'center' }); mainBtn.focus(); } }); } /* ===================== UTIL ===================== */ function formatPrice(num) { return num.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }); } /* ===================== INIT ===================== */ document.addEventListener('DOMContentLoaded', () => { updateCartUI(); initGallery(); initSizeSelector(); initAddToCart(); initMobileMenu(); if (document.getElementById('cart-items')) renderCartPage(); if (document.getElementById('checkout-form')) initCheckout(); if (document.body.dataset.page === 'product') initStickyMobileATC(); if (document.getElementById('insta-grid')) initInstaFeed(); });