Kundenrezensionen jetzt vollständig durch Kund:innen selbst, nicht mehr über Admin
Bisher musste VanVan Bewertungen manuell im CMS eintragen. Jetzt können angemeldete Kund:innen Rezensionen direkt auf der Produktseite abgeben (inkl. Herzen-Sternebewertung und Text). Die Durchschnittsbewertung wird automatisch berechnet und überall live eingebunden: Produktkarten (shop-weit), Produktdetailseite, Shop-Filter/Sortierung nach Bewertung und die Kundenstimmen-Sektion auf der Startseite. Neu dazu: ein Kontobereich fürs Login (Demo-Login per E-Mail), Profilfoto- Upload, wählbarer Benutzername, und ein Header-Kontomenü, das nur bei Anmeldung erscheint. Jede Bestellung im Konto-Dashboard hat einen "Rezension schreiben"-Link, der direkt zur Produktseite führt und dort per Bestellnummer verknüpft wird; im Profil ist dann sofort sichtbar, welche Bestellungen schon bewertet wurden. Technisch als reines Frontend/localStorage-System umgesetzt (kein Backend in dieser Phase), analog zum bestehenden Warenkorb-Muster. Admin-CMS-Feld für manuelle Bewertungen entfernt, da Rezensionen jetzt ausschließlich von Kund:innen kommen (auch aus rechtlichen Gründen: keine Fake-Bewertungen). Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d27a858d8f
commit
6f10e41386
@@ -78,12 +78,30 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||||
<span class="install-label">{t.common.installAppShort}</span>
|
||||
</button>
|
||||
<LanguageSwitcher lang={lang} path={path} legalOnly={legalOnly} />
|
||||
<a href={p("/konto/")} class="account-btn">
|
||||
<span class="account-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.4 0-9 2.2-9 5v2h18v-2c0-2.8-4.6-5-9-5Z"/></svg>
|
||||
</span>
|
||||
<span class="account-label">{t.nav.login}</span>
|
||||
</a>
|
||||
{/* Zwei Zustände nebeneinander im HTML, per JS umgeschaltet (siehe Script unten) — ohne
|
||||
eingebautes Login (Phase 1: nur clientseitig im localStorage, siehe scripts/account.ts)
|
||||
weiß der Server beim Rendern nie, ob jemand "angemeldet" ist. Standardmäßig ist nur der
|
||||
Anmelden-Link sichtbar; sobald JS geladen hat und ein lokales Konto findet, blendet es
|
||||
auf das Konto-Menü um. */}
|
||||
<div class="account-area" id="account-area">
|
||||
<a href={p("/konto/")} class="account-btn" id="account-login-btn">
|
||||
<span class="account-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.4 0-9 2.2-9 5v2h18v-2c0-2.8-4.6-5-9-5Z"/></svg>
|
||||
</span>
|
||||
<span class="account-label">{t.nav.login}</span>
|
||||
</a>
|
||||
<div class="account-menu" id="account-menu" style="display:none;">
|
||||
<button type="button" class="account-menu-btn" id="account-menu-btn" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="account-avatar" id="account-avatar" aria-hidden="true"></span>
|
||||
<span class="account-username" id="account-username-label"></span>
|
||||
<span class="chevron" aria-hidden="true">▼</span>
|
||||
</button>
|
||||
<div class="account-dropdown" id="account-dropdown" role="menu">
|
||||
<a href={p("/konto/angemeldet/")} role="menuitem">👤 {t.accountMenu.myAccount}</a>
|
||||
<button type="button" id="account-logout-btn" role="menuitem">🚪 {t.accountMenu.logout}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href={p("/warenkorb/")} class="icon-link cart-link" aria-label={t.nav.account}>
|
||||
🧺 <span class="cart-count" id="cart-count">0</span>
|
||||
</a>
|
||||
@@ -158,6 +176,8 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||||
|
||||
<script>
|
||||
import { cartCount } from "../scripts/cart";
|
||||
import { getAccount, logout } from "../scripts/account";
|
||||
|
||||
function updateCount() {
|
||||
const el = document.getElementById("cart-count");
|
||||
if (el) el.textContent = String(cartCount());
|
||||
@@ -166,6 +186,58 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||||
window.addEventListener("cart:changed", updateCount);
|
||||
window.addEventListener("storage", updateCount);
|
||||
|
||||
// Konto-Bereich der Kopfzeile: "Anmelden"-Link vs. Konto-Menü, je nachdem ob auf diesem
|
||||
// Gerät gerade ein (Demo-)Konto angemeldet ist (siehe scripts/account.ts). Läuft auf JEDER
|
||||
// Seite, weil Layout.astro von allen Seiten eingebunden wird — daher überall konsistent.
|
||||
const loginBtn = document.getElementById("account-login-btn");
|
||||
const menu = document.getElementById("account-menu");
|
||||
const menuBtn = document.getElementById("account-menu-btn");
|
||||
const dropdown = document.getElementById("account-dropdown");
|
||||
const avatar = document.getElementById("account-avatar");
|
||||
const usernameLabel = document.getElementById("account-username-label");
|
||||
const logoutBtn = document.getElementById("account-logout-btn");
|
||||
|
||||
function renderAccountArea() {
|
||||
const a = getAccount();
|
||||
if (loginBtn) loginBtn.style.display = a.loggedIn ? "none" : "";
|
||||
if (menu) menu.style.display = a.loggedIn ? "flex" : "none";
|
||||
if (!a.loggedIn) return;
|
||||
if (usernameLabel) usernameLabel.textContent = a.username || "Konto";
|
||||
if (avatar) {
|
||||
if (a.photo) {
|
||||
avatar.style.backgroundImage = `url(${a.photo})`;
|
||||
avatar.textContent = "";
|
||||
} else {
|
||||
avatar.style.backgroundImage = "";
|
||||
avatar.textContent = (a.username || "?").trim().charAt(0).toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
renderAccountArea();
|
||||
window.addEventListener("account:changed", renderAccountArea);
|
||||
window.addEventListener("storage", renderAccountArea);
|
||||
|
||||
menuBtn?.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const open = menu?.classList.toggle("open");
|
||||
menuBtn.setAttribute("aria-expanded", open ? "true" : "false");
|
||||
});
|
||||
document.addEventListener("click", () => {
|
||||
menu?.classList.remove("open");
|
||||
menuBtn?.setAttribute("aria-expanded", "false");
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") {
|
||||
menu?.classList.remove("open");
|
||||
menuBtn?.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
});
|
||||
logoutBtn?.addEventListener("click", () => {
|
||||
logout();
|
||||
menu?.classList.remove("open");
|
||||
menuBtn?.setAttribute("aria-expanded", "false");
|
||||
});
|
||||
|
||||
const hamburger = document.getElementById("hamburger");
|
||||
const mobileNav = document.getElementById("mobile-nav");
|
||||
hamburger?.addEventListener("click", () => {
|
||||
|
||||
Reference in New Issue
Block a user