import { data, useActionData, useLoaderData } from "react-router"; import { Resend } from "resend"; import { Turnstile } from "@marsidev/react-turnstile"; import { MessageCircle, Mail, CheckCircle } from "lucide-react"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Textarea } from "~/components/ui/textarea"; import type { Route } from "./+types/support"; const SUBJECT_MAX = 200; const MESSAGE_MAX = 5000; export function loader() { return { turnstileSiteKey: process.env.TURNSTILE_SITE_KEY ?? "" }; } export function meta() { return [ { title: "Support - Brackt" }, { name: "description", content: "Get help with Brackt" }, ]; } export async function action({ request }: Route.ActionArgs) { const formData = await request.formData(); // Honeypot check — silently succeed to not tip off bots if (formData.get("website")) { return data({ success: true as const }); } const name = String(formData.get("name") ?? "").trim(); const email = String(formData.get("email") ?? "").trim(); const subject = String(formData.get("subject") ?? "").trim(); const message = String(formData.get("message") ?? "").trim(); const turnstileToken = String(formData.get("cf-turnstile-response") ?? ""); // Basic validation if (!name || !email || !subject || !message) { return data({ error: "All fields are required." }); } if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { return data({ error: "Please enter a valid email address." }); } if (subject.length > SUBJECT_MAX) { return data({ error: `Subject must be ${SUBJECT_MAX} characters or fewer.` }); } if (message.length > MESSAGE_MAX) { return data({ error: `Message must be ${MESSAGE_MAX} characters or fewer.` }); } // Turnstile verification const turnstileSecret = process.env.TURNSTILE_SECRET_KEY; if (turnstileSecret) { if (!turnstileToken) { return data({ error: "Please complete the verification." }); } const verifyResponse = await fetch( "https://challenges.cloudflare.com/turnstile/v0/siteverify", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ secret: turnstileSecret, response: turnstileToken, }), } ); const verifyResult = (await verifyResponse.json()) as { success: boolean }; if (!verifyResult.success) { return data({ error: "Verification failed. Please try again." }); } } // Send email via Resend const resendKey = process.env.RESEND_API_KEY; if (!resendKey) { return data({ error: "Server configuration error. Please try again later." }); } const resend = new Resend(resendKey); const { error: sendError } = await resend.emails.send({ from: "Brackt Support Form ", to: "support@brackt.com", replyTo: email, subject: `[Support] ${subject}`, text: `Name: ${name}\nEmail: ${email}\n\n${message}`, }); if (sendError) { return data({ error: "Failed to send your message. Please try again." }); } return data({ success: true as const }); } export default function Support() { const { turnstileSiteKey } = useLoaderData(); const actionData = useActionData(); const success = actionData && "success" in actionData; const error = actionData && "error" in actionData ? actionData.error : null; return (

Support

Need help? Join our Discord community for the fastest response, or send us a message directly.

{/* Discord */}

Discord Community

The fastest way to get help is to join our Discord server. You can ask questions, report bugs, and chat with other Brackt players.

{/* Contact Form */}

Send a Message

Prefer email? Fill out the form below and we'll get back to you.

{success ? (

Message sent! We'll get back to you as soon as we can.

) : (
{/* Honeypot — visually and positionally hidden; not using display:none so bots can't trivially detect it */}