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

+11 -9
View File
@@ -6,6 +6,7 @@ import { logAdminAction } from "@/lib/audit-log";
import { getCompanyInfo } from "@/lib/company-info";
import { createDhlShipmentLabel, DhlApiError, DhlNotConfiguredError, type DhlCreateShipmentResult } from "@/lib/dhl";
import { requireAdmin, fail, str } from "./_shared";
import { setFlash } from "@/lib/flash";
// Platzhalterwert, waehrend eine Versandmarken-Anfrage bei DHL laeuft — siehe
// Kommentar bei der Serializable-Transaktion unten. Kein echtes DHL-Format,
@@ -34,17 +35,17 @@ export async function createDhlLabelAction(formData: FormData): Promise<void> {
},
});
if (!order) {
fail("/admin/bestellungen", "Bestellung nicht gefunden.");
return fail("/admin/bestellungen", "Bestellung nicht gefunden.");
}
if (order.fulfillmentMethod !== "VERSAND") {
fail(detailPath, "Diese Bestellung ist zur Abholung markiert, kein Versand per DHL nötig.");
return fail(detailPath, "Diese Bestellung ist zur Abholung markiert, kein Versand per DHL nötig.");
}
if (order.payment?.status !== "SUCCEEDED") {
fail(detailPath, "Die Bestellung ist noch nicht bezahlt — es kann keine Versandmarke erstellt werden.");
return fail(detailPath, "Die Bestellung ist noch nicht bezahlt — es kann keine Versandmarke erstellt werden.");
}
if (order.shipment?.trackingNumber) {
fail(detailPath, "Für diese Bestellung wurde bereits eine DHL-Sendungsnummer erfasst.");
return fail(detailPath, "Für diese Bestellung wurde bereits eine DHL-Sendungsnummer erfasst.");
}
// Atomarer Claim VOR dem (kostenpflichtigen) DHL-Aufruf: der obige
@@ -76,7 +77,7 @@ export async function createDhlLabelAction(formData: FormData): Promise<void> {
claimed = false;
}
if (!claimed) {
fail(detailPath, "Für diese Bestellung wurde bereits eine DHL-Sendungsnummer erfasst oder eine Anfrage läuft gerade.");
return fail(detailPath, "Für diese Bestellung wurde bereits eine DHL-Sendungsnummer erfasst oder eine Anfrage läuft gerade.");
}
const totalWeightGrams = order.items.reduce((sum, item) => sum + item.weightGrams * item.quantity, 0);
@@ -126,13 +127,13 @@ export async function createDhlLabelAction(formData: FormData): Promise<void> {
.catch(() => {});
if (error instanceof DhlNotConfiguredError) {
fail(detailPath, error.message);
return fail(detailPath, error.message);
}
if (error instanceof DhlApiError) {
fail(detailPath, error.message);
return fail(detailPath, error.message);
}
const message = error instanceof Error ? error.message : "Unbekannter Fehler";
fail(detailPath, `DHL-Versandmarke konnte nicht erstellt werden: ${message}`);
return fail(detailPath, `DHL-Versandmarke konnte nicht erstellt werden: ${message}`);
}
try {
@@ -153,7 +154,7 @@ export async function createDhlLabelAction(formData: FormData): Promise<void> {
`createDhlLabelAction: DHL-Sendungsnummer ${result.trackingNumber} fuer Bestellung ${order.orderNumber} (${orderId}) konnte nicht gespeichert werden — manuelle Nacherfassung noetig.`,
error
);
fail(
return fail(
detailPath,
"Die DHL-Versandmarke wurde erstellt, aber die Sendungsnummer konnte nicht gespeichert werden. Bitte Support kontaktieren, um sie manuell nachzutragen."
);
@@ -168,4 +169,5 @@ export async function createDhlLabelAction(formData: FormData): Promise<void> {
revalidatePath("/admin/bestellungen");
revalidatePath(detailPath);
await setFlash("success", `DHL-Versandmarke erstellt. Sendungsnummer: ${result.trackingNumber}`);
}