Konto: Rechnung direkt auf der Seite ansehen statt nur Download-Platzhalter

Der bisherige "Rechnung (PDF)"-Knopf war ein toter Link (href="#"). Jetzt
öffnet "Rechnung ansehen" pro Bestellung ein natives Dialog-Fenster direkt
auf der Seite mit vollständiger Rechnung: Rechnungsnummer/-datum,
Verkäufer, Rechnungsempfänger (live mit dem aktuellen Kontonamen befüllt),
Artikeltabelle mit Einzel-/Gesamtpreisen und dem §19-UStG-Hinweis — kein
Herunterladen nötig, schließbar per Kreuz, Schließen-Knopf oder Klick auf
den abgedunkelten Hintergrund.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
qcigano
2026-08-03 14:10:25 +02:00
co-authored by Claude Sonnet 5
parent fac404190f
commit e2a19db663
6 changed files with 402 additions and 8 deletions
+20 -4
View File
@@ -229,7 +229,11 @@ export const ui = {
noArrivedOrders: "Noch keine angekommenen Bestellungen — hier erscheinen deine Bestellungen, sobald sie geliefert wurden.",
ordersTitle: "📦 Meine Bestellungen",
orderNo: "Bestellung", orderDate: "Bestellt am", orderTotal: "Gesamt", orderItems: "Artikel",
trackingLabel: "Sendungsverfolgung", trackingBtn: "Sendung verfolgen →", invoiceBtn: "Rechnung (PDF)", reorderBtn: "Nochmal bestellen",
trackingLabel: "Sendungsverfolgung", trackingBtn: "Sendung verfolgen →", invoiceBtn: "Rechnung ansehen", reorderBtn: "Nochmal bestellen",
invoiceTitle: "Rechnung", invoiceNumberLabel: "Rechnungsnummer", invoiceDateLabel: "Rechnungsdatum",
invoiceSellerLabel: "Verkäufer", invoiceBillToLabel: "Rechnungsempfänger",
invoiceItemLabel: "Artikel", invoiceQtyLabel: "Menge", invoiceUnitPriceLabel: "Einzelpreis", invoiceSumLabel: "Gesamtbetrag",
invoiceCloseBtn: "Schließen",
statusBezahlt: "Bezahlt", statusBearbeitung: "In Bearbeitung", statusVersandVorbereitet: "Versand vorbereitet", statusVersendet: "Versendet", statusAbgeschlossen: "Abgeschlossen",
wishlistTitle: "🩵 Meine Wunschliste", wishlistEmpty: "Deine Wunschliste ist noch leer — stöbere im Shop und speichere deine Lieblingsstücke mit dem Herz-Symbol.",
recentTitle: "🕘 Zuletzt angesehen",
@@ -463,7 +467,11 @@ export const ui = {
noArrivedOrders: "No delivered orders yet — your orders will appear here once they've arrived.",
ordersTitle: "📦 My orders",
orderNo: "Order", orderDate: "Ordered on", orderTotal: "Total", orderItems: "Items",
trackingLabel: "Tracking", trackingBtn: "Track shipment →", invoiceBtn: "Invoice (PDF)", reorderBtn: "Order again",
trackingLabel: "Tracking", trackingBtn: "Track shipment →", invoiceBtn: "View invoice", reorderBtn: "Order again",
invoiceTitle: "Invoice", invoiceNumberLabel: "Invoice number", invoiceDateLabel: "Invoice date",
invoiceSellerLabel: "Seller", invoiceBillToLabel: "Billed to",
invoiceItemLabel: "Item", invoiceQtyLabel: "Qty", invoiceUnitPriceLabel: "Unit price", invoiceSumLabel: "Total amount",
invoiceCloseBtn: "Close",
statusBezahlt: "Paid", statusBearbeitung: "In progress", statusVersandVorbereitet: "Preparing shipment", statusVersendet: "Shipped", statusAbgeschlossen: "Completed",
wishlistTitle: "🩵 My wishlist", wishlistEmpty: "Your wishlist is still empty — browse the shop and save your favourites with the heart icon.",
recentTitle: "🕘 Recently viewed",
@@ -697,7 +705,11 @@ export const ui = {
noArrivedOrders: "No kei aacho Bstellige — da erschine dini Bstellige, sobald sie glifert worde sind.",
ordersTitle: "📦 Mini Bstellige",
orderNo: "Bstellig", orderDate: "Bstellt am", orderTotal: "Total", orderItems: "Artikel",
trackingLabel: "Sändigsverfolgig", trackingBtn: "Sändig verfolge →", invoiceBtn: "Rächnig (PDF)", reorderBtn: "Nomal bstelle",
trackingLabel: "Sändigsverfolgig", trackingBtn: "Sändig verfolge →", invoiceBtn: "Rächnig aluege", reorderBtn: "Nomal bstelle",
invoiceTitle: "Rächnig", invoiceNumberLabel: "Rächnigsnummere", invoiceDateLabel: "Rächnigsdatum",
invoiceSellerLabel: "Verchöifer", invoiceBillToLabel: "Rächnigsempfänger",
invoiceItemLabel: "Artikel", invoiceQtyLabel: "Mengi", invoiceUnitPriceLabel: "Ystückspreis", invoiceSumLabel: "Gsamtbetrag",
invoiceCloseBtn: "Schlüsse",
statusBezahlt: "Zahlt", statusBearbeitung: "I dr Bearbeitig", statusVersandVorbereitet: "Versand vorbereitet", statusVersendet: "Verschickt", statusAbgeschlossen: "Abgschlosse",
wishlistTitle: "🩵 Mini Wunschlischte", wishlistEmpty: "Dini Wunschlischte isch no leer — lueg im Shop verbi und spicher dini Lieblingssächeli mit em Härz-Symbol.",
recentTitle: "🕘 Zletscht aagluegt",
@@ -931,7 +943,11 @@ export const ui = {
noArrivedOrders: "Aucune commande livrée pour le moment — vos commandes apparaîtront ici dès qu'elles seront arrivées.",
ordersTitle: "📦 Mes commandes",
orderNo: "Commande", orderDate: "Commandé le", orderTotal: "Total", orderItems: "Articles",
trackingLabel: "Suivi", trackingBtn: "Suivre l'envoi →", invoiceBtn: "Facture (PDF)", reorderBtn: "Commander à nouveau",
trackingLabel: "Suivi", trackingBtn: "Suivre l'envoi →", invoiceBtn: "Voir la facture", reorderBtn: "Commander à nouveau",
invoiceTitle: "Facture", invoiceNumberLabel: "Numéro de facture", invoiceDateLabel: "Date de facture",
invoiceSellerLabel: "Vendeur", invoiceBillToLabel: "Facturé à",
invoiceItemLabel: "Article", invoiceQtyLabel: "Qté", invoiceUnitPriceLabel: "Prix unitaire", invoiceSumLabel: "Montant total",
invoiceCloseBtn: "Fermer",
statusBezahlt: "Payée", statusBearbeitung: "En préparation", statusVersandVorbereitet: "Envoi en préparation", statusVersendet: "Expédiée", statusAbgeschlossen: "Terminée",
wishlistTitle: "🩵 Ma liste de souhaits", wishlistEmpty: "Votre liste de souhaits est encore vide — parcourez la boutique et enregistrez vos coups de cœur avec l'icône en forme de cœur.",
recentTitle: "🕘 Vus récemment",
+83 -1
View File
@@ -141,13 +141,72 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
<div class="order-actions">
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
<button type="button" class="btn btn-outline btn-sm" data-open-invoice={`invoice-${b.nummer}`}>{t.accountDash.invoiceBtn}</button>
<a class="btn btn-outline btn-sm" href="/ch/shop/">{t.accountDash.reorderBtn}</a>
</div>
</div>
{b.tracking && (
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
)}
{/* Rechnung direkt auf der Seite ansehen (natives <dialog>, siehe Skript unten) —
kein Download nötig. Empfängername wird beim Öffnen live aus dem Konto befüllt
(siehe .invoice-billto-name), Adresse ist bewusst dieselbe Platzhalter-Adresse
wie oben unter "Meine Daten". */}
<dialog class="invoice-dialog" id={`invoice-${b.nummer}`}>
<div class="invoice-card">
<div class="invoice-head">
<div>
<h3>{t.accountDash.invoiceTitle}</h3>
<p class="invoice-meta">{t.accountDash.invoiceNumberLabel}: {b.nummer}</p>
<p class="invoice-meta">{t.accountDash.invoiceDateLabel}: {b.datum}</p>
</div>
<button type="button" class="invoice-close" data-close-invoice aria-label={t.accountDash.invoiceCloseBtn}>✕</button>
</div>
<div class="invoice-parties">
<div>
<h4>{t.accountDash.invoiceSellerLabel}</h4>
<p>Van's DIY &amp; Bastelbedarf</p>
</div>
<div>
<h4>{t.accountDash.invoiceBillToLabel}</h4>
<p><span class="invoice-billto-name"></span><br />Musterstraße 1<br />10115 Berlin<br />Deutschland</p>
</div>
</div>
<table class="invoice-table">
<thead>
<tr>
<th>{t.accountDash.invoiceItemLabel}</th>
<th>{t.accountDash.invoiceQtyLabel}</th>
<th>{t.accountDash.invoiceUnitPriceLabel}</th>
<th>{t.accountDash.invoiceSumLabel}</th>
</tr>
</thead>
<tbody>
{b.artikel.map((a) => (
<tr>
<td>{a.name}</td>
<td>{a.menge}</td>
<td>{formatPrice(a.preis, lang)}</td>
<td>{formatPrice(a.preis * a.menge, lang)}</td>
</tr>
))}
<tr class="invoice-total-row">
<td colspan="3">{t.accountDash.orderTotal}</td>
<td>{formatPrice(b.summe, lang)}</td>
</tr>
</tbody>
</table>
<p class="invoice-vat-note">{t.common.vatNote}</p>
<div class="invoice-foot">
<button type="button" class="btn btn-outline btn-sm" data-close-invoice>{t.accountDash.invoiceCloseBtn}</button>
</div>
</div>
</dialog>
</div>
))}
</div>
@@ -316,6 +375,10 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
el.style.backgroundImage = bg;
el.textContent = text;
});
// Empfängername in allen (schon gerenderten) Rechnungs-Dialogen live mit aktualisieren.
document.querySelectorAll<HTMLElement>(".invoice-billto-name").forEach((el) => {
el.textContent = anzeigeName(a) || fallbackName;
});
}
renderProfil();
window.addEventListener("account:changed", renderProfil);
@@ -351,6 +414,25 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.location.href = loginPath;
});
// Rechnung direkt auf der Seite ansehen: natives <dialog> pro Bestellung, geöffnet/geschlossen
// per Button — kein Download nötig, kein Verlassen der Seite.
document.querySelectorAll<HTMLButtonElement>("[data-open-invoice]").forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.openInvoice;
const dialog = id ? (document.getElementById(id) as HTMLDialogElement | null) : null;
dialog?.showModal();
});
});
document.querySelectorAll<HTMLDialogElement>(".invoice-dialog").forEach((dialog) => {
dialog.querySelectorAll<HTMLButtonElement>("[data-close-invoice]").forEach((btn) => {
btn.addEventListener("click", () => dialog.close());
});
// Klick auf den abgedunkelten Hintergrund schließt den Dialog ebenfalls.
dialog.addEventListener("click", (e) => {
if (e.target === dialog) dialog.close();
});
});
// Pro Bestellposition: Button "Rezension schreiben" durch "✓ Bewertet" ersetzen, sobald für
// genau diese Bestellung+Produkt schon eine Rezension existiert (siehe scripts/reviews.ts,
// wird beim Absenden des Formulars auf der Produktseite markiert).
+83 -1
View File
@@ -141,13 +141,72 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
<div class="order-actions">
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
<button type="button" class="btn btn-outline btn-sm" data-open-invoice={`invoice-${b.nummer}`}>{t.accountDash.invoiceBtn}</button>
<a class="btn btn-outline btn-sm" href="/en/shop/">{t.accountDash.reorderBtn}</a>
</div>
</div>
{b.tracking && (
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
)}
{/* Rechnung direkt auf der Seite ansehen (natives <dialog>, siehe Skript unten) —
kein Download nötig. Empfängername wird beim Öffnen live aus dem Konto befüllt
(siehe .invoice-billto-name), Adresse ist bewusst dieselbe Platzhalter-Adresse
wie oben unter "Meine Daten". */}
<dialog class="invoice-dialog" id={`invoice-${b.nummer}`}>
<div class="invoice-card">
<div class="invoice-head">
<div>
<h3>{t.accountDash.invoiceTitle}</h3>
<p class="invoice-meta">{t.accountDash.invoiceNumberLabel}: {b.nummer}</p>
<p class="invoice-meta">{t.accountDash.invoiceDateLabel}: {b.datum}</p>
</div>
<button type="button" class="invoice-close" data-close-invoice aria-label={t.accountDash.invoiceCloseBtn}>✕</button>
</div>
<div class="invoice-parties">
<div>
<h4>{t.accountDash.invoiceSellerLabel}</h4>
<p>Van's DIY &amp; Bastelbedarf</p>
</div>
<div>
<h4>{t.accountDash.invoiceBillToLabel}</h4>
<p><span class="invoice-billto-name"></span><br />Musterstraße 1<br />10115 Berlin<br />Deutschland</p>
</div>
</div>
<table class="invoice-table">
<thead>
<tr>
<th>{t.accountDash.invoiceItemLabel}</th>
<th>{t.accountDash.invoiceQtyLabel}</th>
<th>{t.accountDash.invoiceUnitPriceLabel}</th>
<th>{t.accountDash.invoiceSumLabel}</th>
</tr>
</thead>
<tbody>
{b.artikel.map((a) => (
<tr>
<td>{a.name}</td>
<td>{a.menge}</td>
<td>{formatPrice(a.preis, lang)}</td>
<td>{formatPrice(a.preis * a.menge, lang)}</td>
</tr>
))}
<tr class="invoice-total-row">
<td colspan="3">{t.accountDash.orderTotal}</td>
<td>{formatPrice(b.summe, lang)}</td>
</tr>
</tbody>
</table>
<p class="invoice-vat-note">{t.common.vatNote}</p>
<div class="invoice-foot">
<button type="button" class="btn btn-outline btn-sm" data-close-invoice>{t.accountDash.invoiceCloseBtn}</button>
</div>
</div>
</dialog>
</div>
))}
</div>
@@ -316,6 +375,10 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
el.style.backgroundImage = bg;
el.textContent = text;
});
// Empfängername in allen (schon gerenderten) Rechnungs-Dialogen live mit aktualisieren.
document.querySelectorAll<HTMLElement>(".invoice-billto-name").forEach((el) => {
el.textContent = anzeigeName(a) || fallbackName;
});
}
renderProfil();
window.addEventListener("account:changed", renderProfil);
@@ -351,6 +414,25 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.location.href = loginPath;
});
// Rechnung direkt auf der Seite ansehen: natives <dialog> pro Bestellung, geöffnet/geschlossen
// per Button — kein Download nötig, kein Verlassen der Seite.
document.querySelectorAll<HTMLButtonElement>("[data-open-invoice]").forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.openInvoice;
const dialog = id ? (document.getElementById(id) as HTMLDialogElement | null) : null;
dialog?.showModal();
});
});
document.querySelectorAll<HTMLDialogElement>(".invoice-dialog").forEach((dialog) => {
dialog.querySelectorAll<HTMLButtonElement>("[data-close-invoice]").forEach((btn) => {
btn.addEventListener("click", () => dialog.close());
});
// Klick auf den abgedunkelten Hintergrund schließt den Dialog ebenfalls.
dialog.addEventListener("click", (e) => {
if (e.target === dialog) dialog.close();
});
});
// Pro Bestellposition: Button "Rezension schreiben" durch "✓ Bewertet" ersetzen, sobald für
// genau diese Bestellung+Produkt schon eine Rezension existiert (siehe scripts/reviews.ts,
// wird beim Absenden des Formulars auf der Produktseite markiert).
+83 -1
View File
@@ -141,13 +141,72 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
<div class="order-actions">
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
<button type="button" class="btn btn-outline btn-sm" data-open-invoice={`invoice-${b.nummer}`}>{t.accountDash.invoiceBtn}</button>
<a class="btn btn-outline btn-sm" href="/fr/shop/">{t.accountDash.reorderBtn}</a>
</div>
</div>
{b.tracking && (
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
)}
{/* Rechnung direkt auf der Seite ansehen (natives <dialog>, siehe Skript unten) —
kein Download nötig. Empfängername wird beim Öffnen live aus dem Konto befüllt
(siehe .invoice-billto-name), Adresse ist bewusst dieselbe Platzhalter-Adresse
wie oben unter "Meine Daten". */}
<dialog class="invoice-dialog" id={`invoice-${b.nummer}`}>
<div class="invoice-card">
<div class="invoice-head">
<div>
<h3>{t.accountDash.invoiceTitle}</h3>
<p class="invoice-meta">{t.accountDash.invoiceNumberLabel}: {b.nummer}</p>
<p class="invoice-meta">{t.accountDash.invoiceDateLabel}: {b.datum}</p>
</div>
<button type="button" class="invoice-close" data-close-invoice aria-label={t.accountDash.invoiceCloseBtn}>✕</button>
</div>
<div class="invoice-parties">
<div>
<h4>{t.accountDash.invoiceSellerLabel}</h4>
<p>Van's DIY &amp; Bastelbedarf</p>
</div>
<div>
<h4>{t.accountDash.invoiceBillToLabel}</h4>
<p><span class="invoice-billto-name"></span><br />Musterstraße 1<br />10115 Berlin<br />Deutschland</p>
</div>
</div>
<table class="invoice-table">
<thead>
<tr>
<th>{t.accountDash.invoiceItemLabel}</th>
<th>{t.accountDash.invoiceQtyLabel}</th>
<th>{t.accountDash.invoiceUnitPriceLabel}</th>
<th>{t.accountDash.invoiceSumLabel}</th>
</tr>
</thead>
<tbody>
{b.artikel.map((a) => (
<tr>
<td>{a.name}</td>
<td>{a.menge}</td>
<td>{formatPrice(a.preis, lang)}</td>
<td>{formatPrice(a.preis * a.menge, lang)}</td>
</tr>
))}
<tr class="invoice-total-row">
<td colspan="3">{t.accountDash.orderTotal}</td>
<td>{formatPrice(b.summe, lang)}</td>
</tr>
</tbody>
</table>
<p class="invoice-vat-note">{t.common.vatNote}</p>
<div class="invoice-foot">
<button type="button" class="btn btn-outline btn-sm" data-close-invoice>{t.accountDash.invoiceCloseBtn}</button>
</div>
</div>
</dialog>
</div>
))}
</div>
@@ -316,6 +375,10 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
el.style.backgroundImage = bg;
el.textContent = text;
});
// Empfängername in allen (schon gerenderten) Rechnungs-Dialogen live mit aktualisieren.
document.querySelectorAll<HTMLElement>(".invoice-billto-name").forEach((el) => {
el.textContent = anzeigeName(a) || fallbackName;
});
}
renderProfil();
window.addEventListener("account:changed", renderProfil);
@@ -351,6 +414,25 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.location.href = loginPath;
});
// Rechnung direkt auf der Seite ansehen: natives <dialog> pro Bestellung, geöffnet/geschlossen
// per Button — kein Download nötig, kein Verlassen der Seite.
document.querySelectorAll<HTMLButtonElement>("[data-open-invoice]").forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.openInvoice;
const dialog = id ? (document.getElementById(id) as HTMLDialogElement | null) : null;
dialog?.showModal();
});
});
document.querySelectorAll<HTMLDialogElement>(".invoice-dialog").forEach((dialog) => {
dialog.querySelectorAll<HTMLButtonElement>("[data-close-invoice]").forEach((btn) => {
btn.addEventListener("click", () => dialog.close());
});
// Klick auf den abgedunkelten Hintergrund schließt den Dialog ebenfalls.
dialog.addEventListener("click", (e) => {
if (e.target === dialog) dialog.close();
});
});
// Pro Bestellposition: Button "Rezension schreiben" durch "✓ Bewertet" ersetzen, sobald für
// genau diese Bestellung+Produkt schon eine Rezension existiert (siehe scripts/reviews.ts,
// wird beim Absenden des Formulars auf der Produktseite markiert).
+83 -1
View File
@@ -141,13 +141,72 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
<div class="order-actions">
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
<button type="button" class="btn btn-outline btn-sm" data-open-invoice={`invoice-${b.nummer}`}>{t.accountDash.invoiceBtn}</button>
<a class="btn btn-outline btn-sm" href="/shop/">{t.accountDash.reorderBtn}</a>
</div>
</div>
{b.tracking && (
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
)}
{/* Rechnung direkt auf der Seite ansehen (natives <dialog>, siehe Skript unten) —
kein Download nötig. Empfängername wird beim Öffnen live aus dem Konto befüllt
(siehe .invoice-billto-name), Adresse ist bewusst dieselbe Platzhalter-Adresse
wie oben unter "Meine Daten". */}
<dialog class="invoice-dialog" id={`invoice-${b.nummer}`}>
<div class="invoice-card">
<div class="invoice-head">
<div>
<h3>{t.accountDash.invoiceTitle}</h3>
<p class="invoice-meta">{t.accountDash.invoiceNumberLabel}: {b.nummer}</p>
<p class="invoice-meta">{t.accountDash.invoiceDateLabel}: {b.datum}</p>
</div>
<button type="button" class="invoice-close" data-close-invoice aria-label={t.accountDash.invoiceCloseBtn}>✕</button>
</div>
<div class="invoice-parties">
<div>
<h4>{t.accountDash.invoiceSellerLabel}</h4>
<p>Van's DIY &amp; Bastelbedarf</p>
</div>
<div>
<h4>{t.accountDash.invoiceBillToLabel}</h4>
<p><span class="invoice-billto-name"></span><br />Musterstraße 1<br />10115 Berlin<br />Deutschland</p>
</div>
</div>
<table class="invoice-table">
<thead>
<tr>
<th>{t.accountDash.invoiceItemLabel}</th>
<th>{t.accountDash.invoiceQtyLabel}</th>
<th>{t.accountDash.invoiceUnitPriceLabel}</th>
<th>{t.accountDash.invoiceSumLabel}</th>
</tr>
</thead>
<tbody>
{b.artikel.map((a) => (
<tr>
<td>{a.name}</td>
<td>{a.menge}</td>
<td>{formatPrice(a.preis, lang)}</td>
<td>{formatPrice(a.preis * a.menge, lang)}</td>
</tr>
))}
<tr class="invoice-total-row">
<td colspan="3">{t.accountDash.orderTotal}</td>
<td>{formatPrice(b.summe, lang)}</td>
</tr>
</tbody>
</table>
<p class="invoice-vat-note">{t.common.vatNote}</p>
<div class="invoice-foot">
<button type="button" class="btn btn-outline btn-sm" data-close-invoice>{t.accountDash.invoiceCloseBtn}</button>
</div>
</div>
</dialog>
</div>
))}
</div>
@@ -316,6 +375,10 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
el.style.backgroundImage = bg;
el.textContent = text;
});
// Empfängername in allen (schon gerenderten) Rechnungs-Dialogen live mit aktualisieren.
document.querySelectorAll<HTMLElement>(".invoice-billto-name").forEach((el) => {
el.textContent = anzeigeName(a) || fallbackName;
});
}
renderProfil();
window.addEventListener("account:changed", renderProfil);
@@ -351,6 +414,25 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.location.href = loginPath;
});
// Rechnung direkt auf der Seite ansehen: natives <dialog> pro Bestellung, geöffnet/geschlossen
// per Button — kein Download nötig, kein Verlassen der Seite.
document.querySelectorAll<HTMLButtonElement>("[data-open-invoice]").forEach((btn) => {
btn.addEventListener("click", () => {
const id = btn.dataset.openInvoice;
const dialog = id ? (document.getElementById(id) as HTMLDialogElement | null) : null;
dialog?.showModal();
});
});
document.querySelectorAll<HTMLDialogElement>(".invoice-dialog").forEach((dialog) => {
dialog.querySelectorAll<HTMLButtonElement>("[data-close-invoice]").forEach((btn) => {
btn.addEventListener("click", () => dialog.close());
});
// Klick auf den abgedunkelten Hintergrund schließt den Dialog ebenfalls.
dialog.addEventListener("click", (e) => {
if (e.target === dialog) dialog.close();
});
});
// Pro Bestellposition: Button "Rezension schreiben" durch "✓ Bewertet" ersetzen, sobald für
// genau diese Bestellung+Produkt schon eine Rezension existiert (siehe scripts/reviews.ts,
// wird beim Absenden des Formulars auf der Produktseite markiert).
+50
View File
@@ -1556,6 +1556,56 @@ a:focus-visible, button:focus-visible {
.order-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.order-tracking-no { margin: 0; color: var(--c-text-muted); }
.order-tracking-no code { font-size: 0.9em; letter-spacing: 0.02em; }
/* Rechnung direkt auf der Seite ansehen (natives <dialog>) — kein Download nötig, öffnet sich
sofort als Overlay über der aktuellen Ansicht. */
.invoice-dialog {
border: none;
padding: 0;
background: transparent;
color: var(--c-text);
max-width: min(94vw, 580px);
width: 100%;
}
.invoice-dialog::backdrop { background: rgba(6, 5, 10, 0.75); backdrop-filter: blur(3px); }
.invoice-card {
background: linear-gradient(165deg, var(--c-anthracite-light), var(--c-anthracite));
border: 1.5px solid rgba(244, 241, 247, 0.16);
border-radius: var(--radius-l);
padding: 1.8rem;
max-height: 85vh;
overflow-y: auto;
box-shadow: var(--shadow-card);
}
.invoice-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; margin-bottom: 1rem; }
.invoice-head h3 { margin: 0; }
.invoice-meta { margin: 0.3rem 0 0; font-size: 0.88rem; color: var(--c-text-muted); }
.invoice-close {
background: none;
border: 1px solid rgba(244, 241, 247, 0.2);
border-radius: 50%;
width: 32px;
height: 32px;
color: var(--c-text);
cursor: pointer;
flex-shrink: 0;
font-size: 1.1rem;
line-height: 1;
}
.invoice-close:hover { border-color: var(--c-accent); color: var(--c-accent); }
.invoice-parties { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; margin: 1.3rem 0; }
.invoice-parties h4 { margin: 0 0 0.35em; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--c-accent); }
.invoice-parties p { margin: 0; font-size: 0.9rem; color: var(--c-text-muted); line-height: 1.55; }
@media (max-width: 480px) { .invoice-parties { grid-template-columns: 1fr; } }
.invoice-table { width: 100%; border-collapse: collapse; margin-top: 0.6rem; }
.invoice-table th, .invoice-table td { text-align: left; padding: 0.55em 0.35em; border-bottom: 1px solid rgba(244, 241, 247, 0.1); font-size: 0.9rem; }
.invoice-table th { color: var(--c-text-muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; }
.invoice-table th:last-child, .invoice-table td:last-child,
.invoice-table th:nth-child(2), .invoice-table td:nth-child(2),
.invoice-table th:nth-child(3), .invoice-table td:nth-child(3) { text-align: right; }
.invoice-total-row td { font-weight: 700; color: var(--c-accent); border-bottom: none; padding-top: 0.8em; font-size: 1rem; }
.invoice-vat-note { margin: 1.1rem 0 0; font-size: 0.8rem; color: var(--c-text-muted); }
.invoice-foot { display: flex; justify-content: flex-end; margin-top: 1.4rem; }
.profile-card { display: flex; flex-direction: column; align-items: stretch; gap: 1.3rem; }
.profile-card .specs { margin-top: 0; width: 100%; }
.profile-photo-row { display: flex; align-items: center; gap: 1.1rem; }