feat: add email notifications, order references, stock badges, and sale flags
- Add customer email field and PayPal payment reminder emails with order reference (HEL-YEAR-ID format) - Display stock availability with color-coded badges (available/low/unavailable) - Add sale/clearance flag with animated red badge overlay - Implement automatic fallback placeholder for missing/broken product images - Add email column to order management view Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
108
index.html
108
index.html
@@ -133,6 +133,50 @@
|
||||
}
|
||||
.card-price { color: var(--accent); font-weight: 800; font-size: 14px; }
|
||||
.card-sub { color: var(--muted); font-size: 12px; }
|
||||
.stock-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.stock-badge.available {
|
||||
background: rgba(123, 213, 141, 0.15);
|
||||
color: var(--ok);
|
||||
border: 1px solid rgba(123, 213, 141, 0.3);
|
||||
}
|
||||
.stock-badge.low {
|
||||
background: rgba(243, 213, 42, 0.15);
|
||||
color: var(--accent);
|
||||
border: 1px solid rgba(243, 213, 42, 0.3);
|
||||
}
|
||||
.stock-badge.unavailable {
|
||||
background: rgba(255, 107, 125, 0.15);
|
||||
color: var(--bad);
|
||||
border: 1px solid rgba(255, 107, 125, 0.3);
|
||||
}
|
||||
.sale-badge {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
background: linear-gradient(135deg, #ff0000, #cc0000);
|
||||
color: #fff;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
box-shadow: 0 4px 12px rgba(255, 0, 0, 0.4);
|
||||
z-index: 10;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
.card-tile { position: relative; }
|
||||
.card-actions { padding: 0 12px 12px; display: flex; justify-content: flex-end; }
|
||||
.detail-btn { width: 100%; }
|
||||
.size-list { display: grid; gap: 8px; padding: 10px 2px 2px; }
|
||||
@@ -422,6 +466,9 @@
|
||||
<label>Handy
|
||||
<input id="fHandy" name="handy" required />
|
||||
</label>
|
||||
<label>E-Mail
|
||||
<input id="fEmail" name="email" type="email" required />
|
||||
</label>
|
||||
<label>Mannschaft
|
||||
<input id="fMannschaft" name="mannschaft" placeholder="z. B. U12" required />
|
||||
</label>
|
||||
@@ -438,6 +485,10 @@
|
||||
<div id="paypalInfo" style="margin-left: 28px; margin-bottom: 12px; font-size: 0.95em; color: #1e40af;">
|
||||
Bitte sende den Betrag über PayPal an: <strong id="paypalAccount">–</strong>
|
||||
</div>
|
||||
<div style="margin-left: 28px; margin-bottom: 12px; padding: 10px; background: #fef3c7; border: 2px solid #f59e0b; border-radius: 6px; font-size: 0.9em; color: #92400e;">
|
||||
<strong>⚠️ WICHTIG:</strong> Bitte unbedingt die Option <strong>"Familie & Freunde"</strong> wählen!<br>
|
||||
Bei normalen PayPal-Zahlungen fallen zusätzliche Gebühren an.
|
||||
</div>
|
||||
<label style="display: flex; align-items: center;">
|
||||
<input type="radio" name="payment_method" value="bar" style="margin-right: 8px;" />
|
||||
<span><strong>Bar bei Abholung</strong></span>
|
||||
@@ -535,18 +586,41 @@ function render(items) {
|
||||
const priceText = price > 0 ? `${price.toFixed(2)} €` : "Preis auf Anfrage";
|
||||
const img = (item.bild_url || "").trim();
|
||||
|
||||
// Verfügbarkeit prüfen
|
||||
const totalStock = Number(t.gezaehlt) || 0;
|
||||
let stockBadge = '';
|
||||
let stockClass = '';
|
||||
|
||||
if (totalStock === 0) {
|
||||
stockBadge = '<div class="stock-badge unavailable">⛔ Nicht verfügbar</div>';
|
||||
stockClass = 'unavailable';
|
||||
} else if (totalStock <= 5) {
|
||||
stockBadge = `<div class="stock-badge low">⚠️ Nur noch ${totalStock} verfügbar</div>`;
|
||||
stockClass = 'low';
|
||||
} else {
|
||||
stockBadge = `<div class="stock-badge available">✓ Verfügbar (${totalStock})</div>`;
|
||||
stockClass = 'available';
|
||||
}
|
||||
|
||||
// Sale-Badge anzeigen
|
||||
const saleBadge = item.sale ? '<div class="sale-badge">SALE 🔥</div>' : '';
|
||||
|
||||
return `
|
||||
<div class="card-tile">
|
||||
${saleBadge}
|
||||
<div class="card-media">
|
||||
${img ? `<button class="thumb-btn" data-img="${img}" aria-label="Bild vergrößern" title="Bild vergrößern" style="width:100%;height:100%;background-image:url('${img}');background-size:cover;background-position:center;border:0;cursor:pointer;"></button>` : "Bild"}
|
||||
${img ? `<button class="thumb-btn" data-img="${img}" aria-label="Bild vergrößern" title="Bild vergrößern" style="width:100%;height:100%;border:0;cursor:pointer;padding:0;position:relative;overflow:hidden;"><img src="${img}" onerror="this.style.display='none';this.nextElementSibling.style.display='flex';" style="width:100%;height:100%;object-fit:cover;display:block;"><div style="display:none;position:absolute;inset:0;align-items:center;justify-content:center;background:linear-gradient(135deg, rgba(42,138,138,.15), rgba(18,45,70,.2));font-size:40px;opacity:0.5;">📦</div></button>` : `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg, rgba(42,138,138,.15), rgba(18,45,70,.2));font-size:40px;opacity:0.5;">📦</div>`}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-title">${item.artikel}</div>
|
||||
<div class="card-price">${priceText}</div>
|
||||
<div class="card-sub">${item.rows.length} Größen</div>
|
||||
${stockBadge}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="order-btn detail-btn" data-artikel="${item.artikel}" data-preis="${priceText}" data-img="${img}">Bestellen / Details</button>
|
||||
<button class="order-btn detail-btn" data-artikel="${item.artikel}" data-preis="${priceText}" data-img="${img}" ${totalStock === 0 ? 'disabled style="opacity: 0.5; cursor: not-allowed;"' : ''}>
|
||||
${totalStock === 0 ? 'Nicht verfügbar' : 'Bestellen / Details'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -607,6 +681,9 @@ document.addEventListener("click", (e) => {
|
||||
}
|
||||
const detailBtn = e.target.closest(".detail-btn");
|
||||
if (detailBtn) {
|
||||
// Verhindere Klick auf disabled Button
|
||||
if (detailBtn.disabled) return;
|
||||
|
||||
const artikel = detailBtn.dataset.artikel;
|
||||
const item = DATA_MAP.get(artikel);
|
||||
if (!item) return;
|
||||
@@ -616,12 +693,27 @@ document.addEventListener("click", (e) => {
|
||||
document.getElementById("detailTitle").textContent = artikel;
|
||||
document.getElementById("detailPrice").textContent = detailBtn.dataset.preis || "Preis auf Anfrage";
|
||||
document.getElementById("detailSizes").textContent = `${item.rows.length} Größen`;
|
||||
const list = item.rows.map(r => `
|
||||
<div class="size-row">
|
||||
<div class="size-meta"><strong>${fmt(r.groesse)}</strong> · Bestand: ${fmt(r.gezaehlt)}</div>
|
||||
${(Number(r.gezaehlt) || 0) > 0 ? `<button class="order-btn" data-artikel="${item.artikel}" data-groesse="${fmt(r.groesse)}">Bestellen</button>` : ""}
|
||||
</div>
|
||||
`).join("");
|
||||
const list = item.rows.map(r => {
|
||||
const stock = Number(r.gezaehlt) || 0;
|
||||
let stockInfo = '';
|
||||
|
||||
if (stock === 0) {
|
||||
stockInfo = '<span class="stock-badge unavailable">Nicht verfügbar</span>';
|
||||
} else if (stock <= 3) {
|
||||
stockInfo = `<span class="stock-badge low">Nur noch ${stock} verfügbar</span>`;
|
||||
} else {
|
||||
stockInfo = `<span class="stock-badge available">Verfügbar (${stock})</span>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="size-row">
|
||||
<div class="size-meta">
|
||||
<strong>${fmt(r.groesse)}</strong> · ${stockInfo}
|
||||
</div>
|
||||
${stock > 0 ? `<button class="order-btn" data-artikel="${item.artikel}" data-groesse="${fmt(r.groesse)}">Bestellen</button>` : '<span style="color: var(--muted); font-size: 11px;">–</span>'}
|
||||
</div>
|
||||
`;
|
||||
}).join("");
|
||||
document.getElementById("detailSizeList").innerHTML = list;
|
||||
document.getElementById("detailModal").classList.add("open");
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user