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", recentTitle: "🕘 Zuletzt angesehen",
profileTitle: "👤 Meine Daten", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadresse", profileEdit: "Daten bearbeiten", profileTitle: "👤 Meine Daten", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadresse", profileEdit: "Daten bearbeiten",
reviewBtn: "🩵 Rezension schreiben", reviewedBadge: "✓ Bewertet", 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", photoLabel: "Profilfoto", photoChange: "Foto ändern", photoRemove: "Entfernen",
}, },
}, },
@@ -462,7 +463,8 @@ export const ui = {
recentTitle: "🕘 Recently viewed", recentTitle: "🕘 Recently viewed",
profileTitle: "👤 My details", profileName: "Name", profileEmail: "Email", profileAddress: "Delivery address", profileEdit: "Edit details", profileTitle: "👤 My details", profileName: "Name", profileEmail: "Email", profileAddress: "Delivery address", profileEdit: "Edit details",
reviewBtn: "🩵 Write a review", reviewedBadge: "✓ Reviewed", 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", photoLabel: "Profile photo", photoChange: "Change photo", photoRemove: "Remove",
}, },
}, },
@@ -692,7 +694,8 @@ export const ui = {
recentTitle: "🕘 Zletscht aagluegt", recentTitle: "🕘 Zletscht aagluegt",
profileTitle: "👤 Mini Date", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadrässe", profileEdit: "Date bearbeite", profileTitle: "👤 Mini Date", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadrässe", profileEdit: "Date bearbeite",
reviewBtn: "🩵 Bewärtig schrybe", reviewedBadge: "✓ Bewärtet", 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", photoLabel: "Profilfoto", photoChange: "Foto ändere", photoRemove: "Entferne",
}, },
}, },
@@ -922,7 +925,8 @@ export const ui = {
recentTitle: "🕘 Vus récemment", recentTitle: "🕘 Vus récemment",
profileTitle: "👤 Mes données", profileName: "Nom", profileEmail: "E-mail", profileAddress: "Adresse de livraison", profileEdit: "Modifier mes données", 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é", 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", 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> <script>
import { cartCount } from "../scripts/cart"; import { cartCount } from "../scripts/cart";
import { getAccount, logout } from "../scripts/account"; import { getAccount, logout, anzeigeName } from "../scripts/account";
function updateCount() { function updateCount() {
const el = document.getElementById("cart-count"); 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 (loginBtn) loginBtn.style.display = a.loggedIn ? "none" : "";
if (menu) menu.style.display = a.loggedIn ? "flex" : "none"; if (menu) menu.style.display = a.loggedIn ? "flex" : "none";
if (!a.loggedIn) return; if (!a.loggedIn) return;
if (usernameLabel) usernameLabel.textContent = a.username || "Konto"; if (usernameLabel) usernameLabel.textContent = anzeigeName(a) || "Konto";
if (avatar) { if (avatar) {
if (a.photo) { if (a.photo) {
avatar.style.backgroundImage = `url(${a.photo})`; avatar.style.backgroundImage = `url(${a.photo})`;
avatar.textContent = ""; avatar.textContent = "";
} else { } else {
avatar.style.backgroundImage = ""; 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>
</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"> <div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label> <label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row"> <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> <button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div> </div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span> <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> </div>
<table class="specs"> <table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText }; window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script> </script>
<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; const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein // 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() { function renderProfil() {
const a = getAccount(); const a = getAccount();
const greetingEl = document.getElementById("dash-greeting"); 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"); const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.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; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username; 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) => { [document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return; if (!el) return;
el.style.backgroundImage = bg; el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil(); renderProfil();
window.addEventListener("account:changed", 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 // Benutzername ändern
const saveBtn = document.getElementById("profile-username-save"); const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout> </Layout>
<script> <script>
import { login, setUsername } from "../../../scripts/account"; import { login, setName } from "../../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null; const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null; const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error"); const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault(); e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null; const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]"); 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/"; window.location.href = "/ch/konto/angemeldet/";
}); });
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script> <script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts"; import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews"; import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../../scripts/account"; import { isLoggedIn, getAccount, anzeigeName } from "../../../scripts/account";
const { const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther, slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!; const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) { if (isLoggedIn()) {
nameField.style.display = "none"; nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`; loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else { } else {
nameField.style.display = ""; nameField.style.display = "";
loginNote.textContent = notLoggedInReview; loginNote.textContent = notLoggedInReview;
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div> </div>
</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"> <div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label> <label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row"> <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> <button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div> </div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span> <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> </div>
<table class="specs"> <table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText }; window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script> </script>
<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; const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein // 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() { function renderProfil() {
const a = getAccount(); const a = getAccount();
const greetingEl = document.getElementById("dash-greeting"); 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"); const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.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; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username; 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) => { [document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return; if (!el) return;
el.style.backgroundImage = bg; el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil(); renderProfil();
window.addEventListener("account:changed", 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 // Benutzername ändern
const saveBtn = document.getElementById("profile-username-save"); const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout> </Layout>
<script> <script>
import { login, setUsername } from "../../../scripts/account"; import { login, setName } from "../../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null; const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null; const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error"); const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault(); e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null; const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]"); 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/"; window.location.href = "/en/konto/angemeldet/";
}); });
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script> <script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts"; import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews"; import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../../scripts/account"; import { isLoggedIn, getAccount, anzeigeName } from "../../../scripts/account";
const { const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther, slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!; const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) { if (isLoggedIn()) {
nameField.style.display = "none"; nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`; loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else { } else {
nameField.style.display = ""; nameField.style.display = "";
loginNote.textContent = notLoggedInReview; loginNote.textContent = notLoggedInReview;
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div> </div>
</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"> <div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label> <label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row"> <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> <button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div> </div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span> <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> </div>
<table class="specs"> <table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText }; window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script> </script>
<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; const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein // 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() { function renderProfil() {
const a = getAccount(); const a = getAccount();
const greetingEl = document.getElementById("dash-greeting"); 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"); const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.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; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username; 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) => { [document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return; if (!el) return;
el.style.backgroundImage = bg; el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil(); renderProfil();
window.addEventListener("account:changed", 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 // Benutzername ändern
const saveBtn = document.getElementById("profile-username-save"); const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout> </Layout>
<script> <script>
import { login, setUsername } from "../../../scripts/account"; import { login, setName } from "../../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null; const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null; const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error"); const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault(); e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null; const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]"); 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/"; window.location.href = "/fr/konto/angemeldet/";
}); });
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script> <script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts"; import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews"; import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../../scripts/account"; import { isLoggedIn, getAccount, anzeigeName } from "../../../scripts/account";
const { const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther, slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!; const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) { if (isLoggedIn()) {
nameField.style.display = "none"; nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`; loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else { } else {
nameField.style.display = ""; nameField.style.display = "";
loginNote.textContent = notLoggedInReview; loginNote.textContent = notLoggedInReview;
+30 -3
View File
@@ -174,6 +174,17 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
</div> </div>
</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"> <div class="profile-field">
<label for="profile-username">{t.accountDash.usernameLabel}</label> <label for="profile-username">{t.accountDash.usernameLabel}</label>
<div class="profile-field-row"> <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> <button type="button" class="btn btn-primary btn-sm" id="profile-username-save">{t.accountDash.usernameSave}</button>
</div> </div>
<span class="small" id="profile-username-status" style="display:none; color: var(--c-accent);">{t.accountDash.usernameSaved}</span> <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> </div>
<table class="specs"> <table class="specs">
@@ -198,7 +210,7 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText }; window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText };
</script> </script>
<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; const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText } = (window as any).__accountDashVars;
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein // 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() { function renderProfil() {
const a = getAccount(); const a = getAccount();
const greetingEl = document.getElementById("dash-greeting"); 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"); const emailEl = document.getElementById("profile-email");
if (emailEl) emailEl.textContent = a.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; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
if (usernameInput && document.activeElement !== usernameInput) usernameInput.value = a.username; 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) => { [document.getElementById("dash-avatar"), document.getElementById("profile-avatar")].forEach((el) => {
if (!el) return; if (!el) return;
el.style.backgroundImage = bg; el.style.backgroundImage = bg;
@@ -235,6 +249,19 @@ const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versend
renderProfil(); renderProfil();
window.addEventListener("account:changed", 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 // Benutzername ändern
const saveBtn = document.getElementById("profile-username-save"); const saveBtn = document.getElementById("profile-username-save");
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null; const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
+2 -2
View File
@@ -102,7 +102,7 @@ const t = useTranslations(lang);
</Layout> </Layout>
<script> <script>
import { login, setUsername } from "../../scripts/account"; import { login, setName } from "../../scripts/account";
const form = document.getElementById("login-form") as HTMLFormElement | null; const form = document.getElementById("login-form") as HTMLFormElement | null;
const emailInput = document.getElementById("login-email") as HTMLInputElement | null; const emailInput = document.getElementById("login-email") as HTMLInputElement | null;
const error = document.getElementById("login-error"); const error = document.getElementById("login-error");
@@ -123,7 +123,7 @@ const t = useTranslations(lang);
e.preventDefault(); e.preventDefault();
const link = document.getElementById("preview-link") as HTMLAnchorElement | null; const link = document.getElementById("preview-link") as HTMLAnchorElement | null;
login("[email protected]"); login("[email protected]");
if (link?.dataset.previewName) setUsername(link.dataset.previewName); if (link?.dataset.previewName) setName(link.dataset.previewName);
window.location.href = "/konto/angemeldet/"; window.location.href = "/konto/angemeldet/";
}); });
+2 -2
View File
@@ -316,7 +316,7 @@ const felder: [string, string | undefined][] = [
<script> <script>
import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../scripts/hearts"; import { heartsHtml, heartsPickerHtml, heartsPickerAktualisieren } from "../../scripts/hearts";
import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../scripts/reviews"; import { bewertungenZusammenfuehren, durchschnitt, fuegeBewertungHinzu, type AnzeigeBewertung } from "../../scripts/reviews";
import { isLoggedIn, getAccount } from "../../scripts/account"; import { isLoggedIn, getAccount, anzeigeName } from "../../scripts/account";
const { const {
slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther, slug, serverReviews, noReviewsText, reviewCountOne, reviewCountOther,
@@ -370,7 +370,7 @@ const felder: [string, string | undefined][] = [
const loginNote = document.getElementById("review-login-note")!; const loginNote = document.getElementById("review-login-note")!;
if (isLoggedIn()) { if (isLoggedIn()) {
nameField.style.display = "none"; nameField.style.display = "none";
loginNote.textContent = `${loginHintReview} ${getAccount().username}`; loginNote.textContent = `${loginHintReview} ${anzeigeName(getAccount())}`;
} else { } else {
nameField.style.display = ""; nameField.style.display = "";
loginNote.textContent = notLoggedInReview; loginNote.textContent = notLoggedInReview;
+23 -4
View File
@@ -7,6 +7,11 @@
export interface Account { export interface Account {
loggedIn: boolean; 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; username: string;
email: string; email: string;
/** Profilfoto als data:-URL (im Browser aus der hochgeladenen Datei erzeugt). */ /** Profilfoto als data:-URL (im Browser aus der hochgeladenen Datei erzeugt). */
@@ -18,7 +23,7 @@ export interface Account {
const KEY = "vandiy_account_v1"; const KEY = "vandiy_account_v1";
function leeresKonto(): Account { function leeresKonto(): Account {
return { loggedIn: false, username: "", email: "", photo: "", bewerteteBestellungen: [] }; return { loggedIn: false, name: "", username: "", email: "", photo: "", bewerteteBestellungen: [] };
} }
export function getAccount(): Account { 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, /** 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 * der ECHTE NAME wird beim ersten Mal aus dem E-Mail-Namen abgeleitet (Platzhalter, bis die
* erhalten (spätere Logins überschreiben einen schon gewählten Benutzernamen nicht). */ * 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 { export function login(email: string): Account {
const bestehend = getAccount(); const bestehend = getAccount();
const account: Account = { const account: Account = {
...bestehend, ...bestehend,
loggedIn: true, loggedIn: true,
email: email.trim(), email: email.trim(),
username: bestehend.username || email.split("@")[0] || "Kundin", name: bestehend.name || email.split("@")[0] || "Kundin",
}; };
saveAccount(account); saveAccount(account);
return account; return account;
@@ -58,10 +64,23 @@ export function logout() {
saveAccount({ ...getAccount(), loggedIn: false }); 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) { export function setUsername(username: string) {
saveAccount({ ...getAccount(), username: username.trim() }); 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) { export function setPhoto(dataUrl: string) {
saveAccount({ ...getAccount(), photo: dataUrl }); 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 // 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. // 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 { export interface LokaleBewertung {
name: string; name: string;
@@ -60,7 +60,7 @@ export function fuegeBewertungHinzu(
): LokaleBewertung { ): LokaleBewertung {
const account = getAccount(); const account = getAccount();
const neu: LokaleBewertung = { 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))), bewertung: Math.min(5, Math.max(1, Math.round(eintrag.bewertung))),
text: eintrag.text.trim(), text: eintrag.text.trim(),
datum: new Date().toISOString().slice(0, 10), datum: new Date().toISOString().slice(0, 10),