feat: sichtbare Rueckmeldung fuer alle Aktionen (Toast statt URL-Meldungen)

Viele Aktionen gaben keine Rueckmeldung (Retoure annehmen, Archiv,
Bewertungen, Adressen, Newsletter, Kundensperre ...) oder meldeten Erfolg,
obwohl die zugehoerige E-Mail nie rausging.

- lib/flash: Meldung als kurzlebiges Cookie; Toast im Root-Layout
  (FlashToast). Ersetzt ?message=/?error= in der URL — darueber liess sich
  bisher beliebiger Text als Systemmeldung im Wabenhain-Layout anzeigen
  (Phishing-Hebel). Server Components leiten ueber /api/hinweis mit festen
  Codes und festen Zielen weiter.
- fail()/succeed() zentral und asynchron; ein Quelltext-Test stellt sicher,
  dass jeder Aufruf mit await/return erfolgt (sonst liefe eine Action nach
  einem Fehler weiter).
- sendMail meldet, ob die Mail angenommen wurde; Aktionen mit Mail zeigen
  ehrlich an, wenn der Versand scheitert (z.B. "Link erneut senden").
- Jede Server Action meldet jetzt Erfolg oder den Grund des Abbruchs.
- SubmitFeedback: geklickter Knopf zeigt sofort einen Ladezustand und ist
  bis zur Antwort gegen Doppelklick gesperrt.
- 2FA: nach dem Einrichten zurueck ins Profil statt auf die Seite mit
  "Deaktivieren".

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-25 04:36:36 +02:00
1 parent fb9c2353c6
commit 839bbff595
71 files changed
+762 -416

No files matched your search

+10 -2
View File
@@ -66,8 +66,14 @@ function getTransport() {
* damit lokale Entwicklung und Deployments ohne Mail-Setup nicht abstuerzen.
* Wirft absichtlich keinen Fehler: ein Mail-Ausfall darf eine Bestellung
* oder Registrierung nicht verhindern.
*
* Liefert true, wenn der Mailserver die Nachricht angenommen hat, sonst
* false (nicht konfiguriert oder Versand fehlgeschlagen). Aktionen mit
* sichtbarer Rueckmeldung ("Link erneut gesendet", "Kundschaft informiert")
* duerfen Erfolg nur melden, wenn hier true zurueckkam — sonst wartet die
* Kundschaft auf eine Mail, die nie kommt.
*/
export async function sendMail(input: SendMailInput): Promise<void> {
export async function sendMail(input: SendMailInput): Promise<boolean> {
const transport = getTransport();
const from = process.env.SMTP_FROM || "Wabenhain <[email protected]>";
@@ -75,7 +81,7 @@ export async function sendMail(input: SendMailInput): Promise<void> {
console.warn(
`[mail] SMTP nicht konfiguriert — E-Mail an ${maskEmail(input.to)} ("${input.subject}") wurde nicht gesendet.`
);
return;
return false;
}
try {
@@ -87,8 +93,10 @@ export async function sendMail(input: SendMailInput): Promise<void> {
text: input.text,
...(input.attachments?.length ? { attachments: input.attachments } : {}),
});
return true;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : "Unbekannter Fehler";
console.error(`[mail] Versand an ${maskEmail(input.to)} fehlgeschlagen: ${message}`);
return false;
}
}