Konto: echter Name jetzt getrennt vom optionalen Benutzernamen (Spitzname)

Bisher gab es nur ein "Benutzername"-Feld, das überall (Begrüßung, Header,
Rezensionen) angezeigt wurde. Jetzt gibt es zwei getrennte Felder: einen
festen "Name" (Pflichtangabe, z.B. für Bestellungen) und einen optionalen
"Benutzername (Spitzname)" als Bonus, falls man lieber mit einem Spitznamen
statt dem echten Namen angezeigt werden möchte.

Überall, wo bisher nur der Benutzername angezeigt wurde (Header-Konto-Menü,
Begrüßung im Dashboard, Rezensions-Autorenname, "angemeldet als"-Hinweis),
gilt jetzt: Spitzname bevorzugt, sonst echter Name als Rückfalloption
(neue anzeigeName()-Hilfsfunktion in scripts/account.ts).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
qcigano
2026-08-03 13:16:04 +02:00
co-authored by Claude Sonnet 5
parent 7549ec239b
commit 5b14630986
16 changed files with 172 additions and 41 deletions
+8 -4
View File
@@ -232,7 +232,8 @@ export const ui = {
recentTitle: "🕘 Zuletzt angesehen",
profileTitle: "👤 Meine Daten", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadresse", profileEdit: "Daten bearbeiten",
reviewBtn: "🩵 Rezension schreiben", reviewedBadge: "✓ Bewertet",
usernameLabel: "Benutzername", usernameSave: "Speichern", usernameSaved: "Gespeichert ✓",
nameSave: "Speichern", nameSaved: "Gespeichert ✓",
usernameLabel: "Benutzername (Spitzname)", usernameHint: "Optional — ein Bonus, falls du lieber mit einem Spitzname statt deinem Namen angezeigt werden willst.", usernameSave: "Speichern", usernameSaved: "Gespeichert ✓",
photoLabel: "Profilfoto", photoChange: "Foto ändern", photoRemove: "Entfernen",
},
},
@@ -462,7 +463,8 @@ export const ui = {
recentTitle: "🕘 Recently viewed",
profileTitle: "👤 My details", profileName: "Name", profileEmail: "Email", profileAddress: "Delivery address", profileEdit: "Edit details",
reviewBtn: "🩵 Write a review", reviewedBadge: "✓ Reviewed",
usernameLabel: "Username", usernameSave: "Save", usernameSaved: "Saved ✓",
nameSave: "Save", nameSaved: "Saved ✓",
usernameLabel: "Username (nickname)", usernameHint: "Optional — a bonus if you'd rather be shown with a nickname instead of your name.", usernameSave: "Save", usernameSaved: "Saved ✓",
photoLabel: "Profile photo", photoChange: "Change photo", photoRemove: "Remove",
},
},
@@ -692,7 +694,8 @@ export const ui = {
recentTitle: "🕘 Zletscht aagluegt",
profileTitle: "👤 Mini Date", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadrässe", profileEdit: "Date bearbeite",
reviewBtn: "🩵 Bewärtig schrybe", reviewedBadge: "✓ Bewärtet",
usernameLabel: "Benutzername", usernameSave: "Spichere", usernameSaved: "Gspicheret ✓",
nameSave: "Spichere", nameSaved: "Gspicheret ✓",
usernameLabel: "Benutzername (Spitzname)", usernameHint: "Optional — es Bonus, falls du liebr mit emene Spitzname statt dim Name aazeigt wirsch.", usernameSave: "Spichere", usernameSaved: "Gspicheret ✓",
photoLabel: "Profilfoto", photoChange: "Foto ändere", photoRemove: "Entferne",
},
},
@@ -922,7 +925,8 @@ export const ui = {
recentTitle: "🕘 Vus récemment",
profileTitle: "👤 Mes données", profileName: "Nom", profileEmail: "E-mail", profileAddress: "Adresse de livraison", profileEdit: "Modifier mes données",
reviewBtn: "🩵 Écrire un avis", reviewedBadge: "✓ Avis publié",
usernameLabel: "Nom d'utilisateur", usernameSave: "Enregistrer", usernameSaved: "Enregistré ✓",
nameSave: "Enregistrer", nameSaved: "Enregistré ✓",
usernameLabel: "Nom d'utilisateur (surnom)", usernameHint: "Facultatif — un bonus si vous préférez être affiché(e) avec un surnom plutôt que votre nom.", usernameSave: "Enregistrer", usernameSaved: "Enregistré ✓",
photoLabel: "Photo de profil", photoChange: "Changer la photo", photoRemove: "Supprimer",
},
},
+3 -3
View File
@@ -176,7 +176,7 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
<script>
import { cartCount } from "../scripts/cart";
import { getAccount, logout } from "../scripts/account";
import { getAccount, logout, anzeigeName } from "../scripts/account";
function updateCount() {
const el = document.getElementById("cart-count");
@@ -202,14 +202,14 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
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 (usernameLabel) usernameLabel.textContent = anzeigeName(a) || "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();
avatar.textContent = (anzeigeName(a) || "?").trim().charAt(0).toUpperCase();
}
}
}
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div>
</div>
{/* Fester Pflicht-Name (z.B. für Bestellungen/Lieferadresse) — getrennt vom optionalen
Benutzernamen weiter unten, der nur ein Spitzname-Bonus ist. */}
<div class="profile-field">
<label for="profile-name">{t.accountDash.profileName}</label>
<div class="profile-field-row">
<input type="text" id="profile-name" maxlength="60" />
<button type="button" class="btn btn-primary btn-sm" id="profile-name-save">{t.accountDash.nameSave}</button>
</div>
<span class="small" id="profile-name-status" style="display:none; color: var(--c-accent);">{t.accountDash.nameSaved}</span>
</div>
<div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row">
@@ -181,6 +192,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span>
<span class="small" style="display:block; color: var(--c-text-muted); margin-top:0.3em;">{t.accountDash.usernameHint}</span>
</div>
<table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script>
<script>
import { getAccount, isLoggedIn, logout, setUsername, setPhoto, hatBestellungBewertet } from "../../../scripts/account";
import { getAccount, isLoggedIn, logout, setName, setUsername, setPhoto, hatBestellungBewertet, anzeigeName } from "../../../scripts/account";
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein
@@ -219,13 +231,15 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
function renderProfil() {
const a = getAccount();
const greetingEl = document.getElementById("dash-greeting");
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", a.username || fallbackName);
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", anzeigeName(a) || fallbackName);
const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.email || "";
const nameInput = document.getElementById("profile-name") as HTMLInputElement | null;
if (nameInput && document.activeElement !== nameInput) nameInput.value = a.name;
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username;
const { bg, text } = avatarHtml(a.username, a.photo);
const { bg, text } = avatarHtml(anzeigeName(a), a.photo);
[document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return;
el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil();
window.addEventListener("account:changed", renderProfil);
// Echter Name ändern (fest, Pflichtfeld)
const nameSaveBtn = document.getElementById("profile-name-save");
const nameInputEl = document.getElementById("profile-name") as HTMLInputElement | null;
const nameStatus = document.getElementById("profile-name-status");
nameSaveBtn?.addEventListener("click", () => {
if (!nameInputEl) return;
setName(nameInputEl.value);
if (nameStatus) {
nameStatus.style.display = "inline";
setTimeout(() => { nameStatus.style.display = "none"; }, 1800);
}
});
// Benutzername ändern
const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout>
<script>
import { login, setUsername } from "../../../scripts/account";
import { login, setName } from "../../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]");
if (link?.dataset.previewName) setUsername(link.dataset.previewName);
if (link?.dataset.previewName) setName(link.dataset.previewName);
window.location.href = "/ch/konto/angemeldet/";
});
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../../scripts/account";
import { isLoggedIn, getAccount, anzeigeName } from "../../../scripts/account";
const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) {
nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`;
loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else {
nameField.style.display = "";
loginNote.textContent = notLoggedInReview;
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div>
</div>
{/* Fester Pflicht-Name (z.B. für Bestellungen/Lieferadresse) — getrennt vom optionalen
Benutzernamen weiter unten, der nur ein Spitzname-Bonus ist. */}
<div class="profile-field">
<label for="profile-name">{t.accountDash.profileName}</label>
<div class="profile-field-row">
<input type="text" id="profile-name" maxlength="60" />
<button type="button" class="btn btn-primary btn-sm" id="profile-name-save">{t.accountDash.nameSave}</button>
</div>
<span class="small" id="profile-name-status" style="display:none; color: var(--c-accent);">{t.accountDash.nameSaved}</span>
</div>
<div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row">
@@ -181,6 +192,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span>
<span class="small" style="display:block; color: var(--c-text-muted); margin-top:0.3em;">{t.accountDash.usernameHint}</span>
</div>
<table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script>
<script>
import { getAccount, isLoggedIn, logout, setUsername, setPhoto, hatBestellungBewertet } from "../../../scripts/account";
import { getAccount, isLoggedIn, logout, setName, setUsername, setPhoto, hatBestellungBewertet, anzeigeName } from "../../../scripts/account";
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein
@@ -219,13 +231,15 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
function renderProfil() {
const a = getAccount();
const greetingEl = document.getElementById("dash-greeting");
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", a.username || fallbackName);
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", anzeigeName(a) || fallbackName);
const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.email || "";
const nameInput = document.getElementById("profile-name") as HTMLInputElement | null;
if (nameInput && document.activeElement !== nameInput) nameInput.value = a.name;
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username;
const { bg, text } = avatarHtml(a.username, a.photo);
const { bg, text } = avatarHtml(anzeigeName(a), a.photo);
[document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return;
el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil();
window.addEventListener("account:changed", renderProfil);
// Echter Name ändern (fest, Pflichtfeld)
const nameSaveBtn = document.getElementById("profile-name-save");
const nameInputEl = document.getElementById("profile-name") as HTMLInputElement | null;
const nameStatus = document.getElementById("profile-name-status");
nameSaveBtn?.addEventListener("click", () => {
if (!nameInputEl) return;
setName(nameInputEl.value);
if (nameStatus) {
nameStatus.style.display = "inline";
setTimeout(() => { nameStatus.style.display = "none"; }, 1800);
}
});
// Benutzername ändern
const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout>
<script>
import { login, setUsername } from "../../../scripts/account";
import { login, setName } from "../../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]");
if (link?.dataset.previewName) setUsername(link.dataset.previewName);
if (link?.dataset.previewName) setName(link.dataset.previewName);
window.location.href = "/en/konto/angemeldet/";
});
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../../scripts/account";
import { isLoggedIn, getAccount, anzeigeName } from "../../../scripts/account";
const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) {
nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`;
loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else {
nameField.style.display = "";
loginNote.textContent = notLoggedInReview;
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div>
</div>
{/* Fester Pflicht-Name (z.B. für Bestellungen/Lieferadresse) — getrennt vom optionalen
Benutzernamen weiter unten, der nur ein Spitzname-Bonus ist. */}
<div class="profile-field">
<label for="profile-name">{t.accountDash.profileName}</label>
<div class="profile-field-row">
<input type="text" id="profile-name" maxlength="60" />
<button type="button" class="btn btn-primary btn-sm" id="profile-name-save">{t.accountDash.nameSave}</button>
</div>
<span class="small" id="profile-name-status" style="display:none; color: var(--c-accent);">{t.accountDash.nameSaved}</span>
</div>
<div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row">
@@ -181,6 +192,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span>
<span class="small" style="display:block; color: var(--c-text-muted); margin-top:0.3em;">{t.accountDash.usernameHint}</span>
</div>
<table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script>
<script>
import { getAccount, isLoggedIn, logout, setUsername, setPhoto, hatBestellungBewertet } from "../../../scripts/account";
import { getAccount, isLoggedIn, logout, setName, setUsername, setPhoto, hatBestellungBewertet, anzeigeName } from "../../../scripts/account";
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein
@@ -219,13 +231,15 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
function renderProfil() {
const a = getAccount();
const greetingEl = document.getElementById("dash-greeting");
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", a.username || fallbackName);
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", anzeigeName(a) || fallbackName);
const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.email || "";
const nameInput = document.getElementById("profile-name") as HTMLInputElement | null;
if (nameInput && document.activeElement !== nameInput) nameInput.value = a.name;
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username;
const { bg, text } = avatarHtml(a.username, a.photo);
const { bg, text } = avatarHtml(anzeigeName(a), a.photo);
[document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return;
el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil();
window.addEventListener("account:changed", renderProfil);
// Echter Name ändern (fest, Pflichtfeld)
const nameSaveBtn = document.getElementById("profile-name-save");
const nameInputEl = document.getElementById("profile-name") as HTMLInputElement | null;
const nameStatus = document.getElementById("profile-name-status");
nameSaveBtn?.addEventListener("click", () => {
if (!nameInputEl) return;
setName(nameInputEl.value);
if (nameStatus) {
nameStatus.style.display = "inline";
setTimeout(() => { nameStatus.style.display = "none"; }, 1800);
}
});
// Benutzername ändern
const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout>
<script>
import { login, setUsername } from "../../../scripts/account";
import { login, setName } from "../../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]");
if (link?.dataset.previewName) setUsername(link.dataset.previewName);
if (link?.dataset.previewName) setName(link.dataset.previewName);
window.location.href = "/fr/konto/angemeldet/";
});
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../../scripts/account";
import { isLoggedIn, getAccount, anzeigeName } from "../../../scripts/account";
const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) {
nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`;
loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else {
nameField.style.display = "";
loginNote.textContent = notLoggedInReview;
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div>
</div>
{/* Fester Pflicht-Name (z.B. für Bestellungen/Lieferadresse) — getrennt vom optionalen
Benutzernamen weiter unten, der nur ein Spitzname-Bonus ist. */}
<div class="profile-field">
<label for="profile-name">{t.accountDash.profileName}</label>
<div class="profile-field-row">
<input type="text" id="profile-name" maxlength="60" />
<button type="button" class="btn btn-primary btn-sm" id="profile-name-save">{t.accountDash.nameSave}</button>
</div>
<span class="small" id="profile-name-status" style="display:none; color: var(--c-accent);">{t.accountDash.nameSaved}</span>
</div>
<div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row">
@@ -181,6 +192,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
<button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span>
<span class="small" style="display:block; color: var(--c-text-muted); margin-top:0.3em;">{t.accountDash.usernameHint}</span>
</div>
<table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script>
<script>
import { getAccount, isLoggedIn, logout, setUsername, setPhoto, hatBestellungBewertet } from "../../scripts/account";
import { getAccount, isLoggedIn, logout, setName, setUsername, setPhoto, hatBestellungBewertet, anzeigeName } from "../../scripts/account";
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein
@@ -219,13 +231,15 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
function renderProfil() {
const a = getAccount();
const greetingEl = document.getElementById("dash-greeting");
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", a.username || fallbackName);
if (greetingEl) greetingEl.textContent = greetingTemplate.replace("{name}", anzeigeName(a) || fallbackName);
const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.email || "";
const nameInput = document.getElementById("profile-name") as HTMLInputElement | null;
if (nameInput && document.activeElement !== nameInput) nameInput.value = a.name;
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username;
const { bg, text } = avatarHtml(a.username, a.photo);
const { bg, text } = avatarHtml(anzeigeName(a), a.photo);
[document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return;
el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil();
window.addEventListener("account:changed", renderProfil);
// Echter Name ändern (fest, Pflichtfeld)
const nameSaveBtn = document.getElementById("profile-name-save");
const nameInputEl = document.getElementById("profile-name") as HTMLInputElement | null;
const nameStatus = document.getElementById("profile-name-status");
nameSaveBtn?.addEventListener("click", () => {
if (!nameInputEl) return;
setName(nameInputEl.value);
if (nameStatus) {
nameStatus.style.display = "inline";
setTimeout(() => { nameStatus.style.display = "none"; }, 1800);
}
});
// Benutzername ändern
const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout>
<script>
import { login, setUsername } from "../../scripts/account";
import { login, setName } from "../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]");
if (link?.dataset.previewName) setUsername(link.dataset.previewName);
if (link?.dataset.previewName) setName(link.dataset.previewName);
window.location.href = "/konto/angemeldet/";
});
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../scripts/account";
import { isLoggedIn, getAccount, anzeigeName } from "../../scripts/account";
const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) {
nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`;
loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else {
nameField.style.display = "";
loginNote.textContent = notLoggedInReview;
+23 -4
View File
@@ -7,6 +7,11 @@
export interface Account {
loggedIn: boolean;
/** Echter Name (fest, z.B. für Bestellungen/Lieferadresse) — Pflichtangabe, unabhängig vom
* optionalen Benutzernamen. */
name: string;
/** Optionaler Spitzname/Bonus-Anzeigename — wird bevorzugt angezeigt (z.B. Begrüßung, Header),
* wenn gesetzt, ersetzt aber NICHT den echten Namen (siehe `name`). */
username: string;
email: string;
/** Profilfoto als data:-URL (im Browser aus der hochgeladenen Datei erzeugt). */
@@ -18,7 +23,7 @@ export interface Account {
const KEY = "vandiy_account_v1";
function leeresKonto(): Account {
return { loggedIn: false, username: "", email: "", photo: "", bewerteteBestellungen: [] };
return { loggedIn: false, name: "", username: "", email: "", photo: "", bewerteteBestellungen: [] };
}
export function getAccount(): Account {
@@ -40,15 +45,16 @@ export function isLoggedIn(): boolean {
}
/** 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). */
* der ECHTE NAME wird beim ersten Mal aus dem E-Mail-Namen abgeleitet (Platzhalter, bis die
* Person ihn selbst einträgt), bleibt danach aber erhalten. Der Benutzername/Spitzname wird
* NICHT automatisch befüllt — er ist ein rein optionaler Bonus, den man sich selbst aussucht. */
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",
name: bestehend.name || email.split("@")[0] || "Kundin",
};
saveAccount(account);
return account;
@@ -58,10 +64,23 @@ export function logout() {
saveAccount({ ...getAccount(), loggedIn: false });
}
/** Echter Name — feste Pflichtangabe (z.B. für Bestellungen/Lieferadresse), unabhängig vom
* optionalen Benutzernamen. */
export function setName(name: string) {
saveAccount({ ...getAccount(), name: name.trim() });
}
/** Optionaler Spitzname/Bonus-Anzeigename — ersetzt NICHT den echten Namen. */
export function setUsername(username: string) {
saveAccount({ ...getAccount(), username: username.trim() });
}
/** Anzeigename für Begrüßung/Header: bevorzugt den gewählten Spitznamen (persönlicher), fällt
* sonst auf den echten Namen zurück. */
export function anzeigeName(a: Account): string {
return a.username || a.name;
}
export function setPhoto(dataUrl: string) {
saveAccount({ ...getAccount(), photo: dataUrl });
}
+2 -2
View File
@@ -6,7 +6,7 @@
// ersetzen (Cloudflare Worker + D1) — bis dahin sieht jede Rezension nur die Person, die sie auf
// ihrem eigenen Gerät/Browser geschrieben hat, wichtig das VanVan gegenüber ehrlich zu kommunizieren.
import { getAccount, markiereBestellungBewertet } from "./account";
import { getAccount, markiereBestellungBewertet, anzeigeName } from "./account";
export interface LokaleBewertung {
name: string;
@@ -60,7 +60,7 @@ export function fuegeBewertungHinzu(
): LokaleBewertung {
const account = getAccount();
const neu: LokaleBewertung = {
name: eintrag.name?.trim() || account.username || "Anonym",
name: eintrag.name?.trim() || anzeigeName(account) || "Anonym",
bewertung: Math.min(5, Math.max(1, Math.round(eintrag.bewertung))),
text: eintrag.text.trim(),
datum: new Date().toISOString().slice(0, 10),