feat: Kontaktformular für ein Erstgespräch
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 4m31s
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 4m31s
Neuer Abschnitt nach den Fragen, im Menü als Erstgespräch. Anfrage geht als E-Mail an die Veranstalter-Adresse (Admin) bzw. ADMIN_NOTIFY_EMAIL, sofort zugestellt, sonst über den Worker wiederholt. Gleicher Schutz wie die Buchung: nur JSON von der eigenen Seite, Rate Limit (5/h je IP), Prüfung auf dem Server, Honeypot gegen Bots, doppeltes Absenden ergibt eine Mail. Keine automatische Mail an die angegebene Adresse (sonst Missbrauch für Spam an Dritte). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
d45700a9ef
commit
7745d5862d
9 files changed
+336
-1
No files matched your search
@@ -3,12 +3,13 @@
|
||||
* bestaetigten Status — eine Teilzahlung heisst nie "vollstaendig bezahlt".
|
||||
*/
|
||||
import type { Booking, Installment } from "@/generated/prisma/client";
|
||||
import type { ContactRequest } from "./contact";
|
||||
import { escapeHtml, type EmailDraft } from "./email";
|
||||
import { appUrl } from "./env";
|
||||
import { formatEuro } from "./money";
|
||||
import { PLAN_LABEL } from "./payment-plan";
|
||||
import { PHASE_LABEL } from "./pricing";
|
||||
import { formatDate } from "./time";
|
||||
import { formatDate, formatDateTime } from "./time";
|
||||
|
||||
type BookingWithInstallments = Booking & { installments: Installment[] };
|
||||
|
||||
@@ -163,3 +164,20 @@ export function reviewCustomerEmail(booking: Booking, token: string): EmailDraft
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/** Anfrage fuer ein Erstgespraech an den Veranstalter (src/lib/contact.ts). */
|
||||
export function contactInquiryEmail(to: string, inquiry: ContactRequest, dedupeKey: string, receivedAt: Date): EmailDraft {
|
||||
const subject = `Anfrage Erstgespräch: ${inquiry.name}`;
|
||||
const lines = [
|
||||
`Über die Webseite kam eine Anfrage für ein Erstgespräch (${formatDateTime(receivedAt)}).`,
|
||||
[
|
||||
`Name: ${inquiry.name}`,
|
||||
`E-Mail: ${inquiry.email}`,
|
||||
`Telefon: ${inquiry.phone ?? "–"}`,
|
||||
`Erreichbar: ${inquiry.availability ?? "–"}`,
|
||||
].join("\n"),
|
||||
inquiry.message ? `Nachricht:\n${inquiry.message}` : "Keine Nachricht angegeben.",
|
||||
];
|
||||
const reply = `mailto:${encodeURIComponent(inquiry.email)}?subject=${encodeURIComponent(`Dein Erstgespräch – ${BRAND}`)}`;
|
||||
return { dedupeKey, to, subject: `${subject} | ${BRAND}`, ...wrap(subject, lines, { href: reply, label: "Antworten" }) };
|
||||
}
|
||||
Reference in new issue
Block a user