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
@@ -0,0 +1,83 @@
|
||||
// Rein clientseitiges "Konto" (Phase 1, kein echtes Login/Backend) — genau wie der Warenkorb
|
||||
// (siehe cart.ts) im localStorage gespeichert. Ermöglicht schon jetzt eine echte, durchgängige
|
||||
// Demo: anmelden, Profil pflegen, Bestellungen bewerten, Kopfzeile reagiert überall darauf.
|
||||
// TODO Phase 2: durch echtes Login (Cloudflare Worker + D1, siehe Tech-Stack-Entscheidung im
|
||||
// Projekt-Vault) ersetzen — Struktur/Feldnamen bewusst schon so gewählt, dass sich eine echte
|
||||
// Session später 1:1 einsetzen lässt, ohne Aufrufstellen in der ganzen Seite anpassen zu müssen.
|
||||
|
||||
export interface Account {
|
||||
loggedIn: boolean;
|
||||
username: string;
|
||||
email: string;
|
||||
/** Profilfoto als data:-URL (im Browser aus der hochgeladenen Datei erzeugt). */
|
||||
photo: string;
|
||||
/** "<bestellnummer>:<produkt-slug>"-Paare, zu denen schon eine Rezension abgegeben wurde. */
|
||||
bewerteteBestellungen: string[];
|
||||
}
|
||||
|
||||
const KEY = "vandiy_account_v1";
|
||||
|
||||
function leeresKonto(): Account {
|
||||
return { loggedIn: false, username: "", email: "", photo: "", bewerteteBestellungen: [] };
|
||||
}
|
||||
|
||||
export function getAccount(): Account {
|
||||
if (typeof localStorage === "undefined") return leeresKonto();
|
||||
try {
|
||||
return { ...leeresKonto(), ...JSON.parse(localStorage.getItem(KEY) || "{}") };
|
||||
} catch {
|
||||
return leeresKonto();
|
||||
}
|
||||
}
|
||||
|
||||
function saveAccount(a: Account) {
|
||||
localStorage.setItem(KEY, JSON.stringify(a));
|
||||
window.dispatchEvent(new CustomEvent("account:changed", { detail: a }));
|
||||
}
|
||||
|
||||
export function isLoggedIn(): boolean {
|
||||
return getAccount().loggedIn;
|
||||
}
|
||||
|
||||
/** Demo-Anmeldung: es gibt noch keine echte Passwortprüfung (Phase 2) — jede E-Mail meldet an,
|
||||
* der Benutzername wird beim ersten Mal aus dem E-Mail-Namen abgeleitet, bleibt danach aber
|
||||
* erhalten (spätere Logins überschreiben einen schon gewählten Benutzernamen nicht). */
|
||||
export function login(email: string): Account {
|
||||
const bestehend = getAccount();
|
||||
const account: Account = {
|
||||
...bestehend,
|
||||
loggedIn: true,
|
||||
email: email.trim(),
|
||||
username: bestehend.username || email.split("@")[0] || "Kundin",
|
||||
};
|
||||
saveAccount(account);
|
||||
return account;
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
saveAccount({ ...getAccount(), loggedIn: false });
|
||||
}
|
||||
|
||||
export function setUsername(username: string) {
|
||||
saveAccount({ ...getAccount(), username: username.trim() });
|
||||
}
|
||||
|
||||
export function setPhoto(dataUrl: string) {
|
||||
saveAccount({ ...getAccount(), photo: dataUrl });
|
||||
}
|
||||
|
||||
export function bestellSchluessel(bestellnummer: string, produktSlug: string): string {
|
||||
return `${bestellnummer}:${produktSlug}`;
|
||||
}
|
||||
|
||||
export function hatBestellungBewertet(bestellnummer: string, produktSlug: string): boolean {
|
||||
return getAccount().bewerteteBestellungen.includes(bestellSchluessel(bestellnummer, produktSlug));
|
||||
}
|
||||
|
||||
export function markiereBestellungBewertet(bestellnummer: string, produktSlug: string) {
|
||||
const a = getAccount();
|
||||
const schluessel = bestellSchluessel(bestellnummer, produktSlug);
|
||||
if (!a.bewerteteBestellungen.includes(schluessel)) {
|
||||
saveAccount({ ...a, bewerteteBestellungen: [...a.bewerteteBestellungen, schluessel] });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user