265 lines
10 KiB
TypeScript
265 lines
10 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { useRouter, useSearchParams } from "next/navigation";
|
||
import { motion, AnimatePresence } from "framer-motion";
|
||
import { toast } from "sonner";
|
||
|
||
export default function RequestPage() {
|
||
const router = useRouter();
|
||
const searchParams = useSearchParams();
|
||
const prefillType = searchParams.get("type") as RequestType | null;
|
||
|
||
const [step, setStep] = useState(1);
|
||
const [loading, setLoading] = useState(false);
|
||
const [form, setForm] = useState({
|
||
type: prefillType || "",
|
||
name: "",
|
||
email: "",
|
||
domain: "",
|
||
company: "",
|
||
message: "",
|
||
hosting: "",
|
||
concern: "",
|
||
});
|
||
|
||
type RequestType =
|
||
| "audit"
|
||
| "consultation"
|
||
| "support"
|
||
| "tool"
|
||
| "partnership";
|
||
|
||
const requestTypes: { id: RequestType; label: string; desc: string; icon: string }[] = [
|
||
{
|
||
id: "audit",
|
||
label: "Free Audit",
|
||
desc: "Request a free website, DNS or performance check.",
|
||
icon: "🧠",
|
||
},
|
||
{
|
||
id: "consultation",
|
||
label: "Consultation / Quote",
|
||
desc: "Discuss a project, hosting setup, or optimization.",
|
||
icon: "⚙️",
|
||
},
|
||
{
|
||
id: "support",
|
||
label: "Technical Support",
|
||
desc: "Report an issue or request hands-on help.",
|
||
icon: "🛠️",
|
||
},
|
||
{
|
||
id: "tool",
|
||
label: "Tool Follow-Up",
|
||
desc: "Continue from one of our free tools or reports.",
|
||
icon: "📊",
|
||
},
|
||
{
|
||
id: "partnership",
|
||
label: "Partnership / Collaboration",
|
||
desc: "Discuss a potential collaboration or integration.",
|
||
icon: "🤝",
|
||
},
|
||
];
|
||
|
||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||
const { name, value } = e.target;
|
||
setForm((f) => ({ ...f, [name]: value }));
|
||
};
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
setLoading(true);
|
||
|
||
try {
|
||
const res = await fetch("/api/request", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(form),
|
||
});
|
||
|
||
if (!res.ok) throw new Error("Submission failed");
|
||
|
||
toast.success("Request submitted successfully!");
|
||
router.push("/request/success");
|
||
} catch (err) {
|
||
console.error(err);
|
||
toast.error("Something went wrong. Please try again.");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<main className="relative isolate min-h-screen bg-gradient-to-b from-neutral-50 via-white to-neutral-100 dark:from-neutral-950 dark:via-neutral-900 dark:to-neutral-950">
|
||
<section className="border-b border-neutral-200 dark:border-neutral-800">
|
||
<div className="container mx-auto max-w-4xl px-6 py-16 text-center">
|
||
<h1 className="text-4xl sm:text-5xl font-bold tracking-tight bg-gradient-to-r from-blue-600 via-purple-600 to-blue-600 bg-clip-text text-transparent">
|
||
Start a Request
|
||
</h1>
|
||
<p className="mt-4 text-neutral-700 dark:text-neutral-300 text-lg">
|
||
Choose what you’d like to do — audits, consultations, or support.
|
||
We’ll guide you through the right steps and get back within one business day.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section className="container mx-auto max-w-3xl px-6 py-12">
|
||
{/* Step 1 – Select Request Type */}
|
||
<AnimatePresence mode="wait">
|
||
{step === 1 && (
|
||
<motion.div
|
||
key="step1"
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -20 }}
|
||
transition={{ duration: 0.3 }}
|
||
>
|
||
<h2 className="text-2xl font-semibold text-neutral-900 dark:text-white mb-6 text-center">
|
||
What kind of request do you have?
|
||
</h2>
|
||
<div className="grid gap-6 sm:grid-cols-2">
|
||
{requestTypes.map((t) => (
|
||
<button
|
||
key={t.id}
|
||
onClick={() => {
|
||
setForm((f) => ({ ...f, type: t.id }));
|
||
setStep(2);
|
||
}}
|
||
className={`rounded-xl border border-neutral-200 dark:border-neutral-800 p-6 text-left hover:shadow-md transition-all duration-200 ${
|
||
form.type === t.id
|
||
? "bg-gradient-to-r from-blue-600 via-purple-600 to-blue-600 text-white"
|
||
: "bg-white dark:bg-neutral-900 text-neutral-900 dark:text-white"
|
||
}`}
|
||
>
|
||
<div className="text-3xl mb-2">{t.icon}</div>
|
||
<h3 className="font-semibold text-lg mb-1">{t.label}</h3>
|
||
<p className="text-sm opacity-80">{t.desc}</p>
|
||
</button>
|
||
))}
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{/* Step 2 – Request Form */}
|
||
{step === 2 && (
|
||
<motion.form
|
||
key="step2"
|
||
onSubmit={handleSubmit}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -20 }}
|
||
transition={{ duration: 0.3 }}
|
||
className="mt-4 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl shadow-sm p-8"
|
||
>
|
||
<h2 className="text-xl font-semibold mb-6 text-neutral-900 dark:text-white text-center">
|
||
{requestTypes.find((t) => t.id === form.type)?.label}
|
||
</h2>
|
||
|
||
<div className="space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-medium mb-1 text-neutral-800 dark:text-neutral-200">
|
||
Name
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="name"
|
||
value={form.name}
|
||
onChange={handleChange}
|
||
className="w-full rounded-lg border border-neutral-300 dark:border-neutral-700 bg-white/80 dark:bg-neutral-900/80 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium mb-1 text-neutral-800 dark:text-neutral-200">
|
||
Email
|
||
</label>
|
||
<input
|
||
type="email"
|
||
name="email"
|
||
value={form.email}
|
||
onChange={handleChange}
|
||
required
|
||
className="w-full rounded-lg border border-neutral-300 dark:border-neutral-700 bg-white/80 dark:bg-neutral-900/80 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
{/* Show domain and technical fields only for audits / consultations */}
|
||
{(form.type === "audit" || form.type === "consultation") && (
|
||
<>
|
||
<div>
|
||
<label className="block text-sm font-medium mb-1 text-neutral-800 dark:text-neutral-200">
|
||
Website or Domain
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="domain"
|
||
placeholder="example.com"
|
||
value={form.domain}
|
||
onChange={handleChange}
|
||
className="w-full rounded-lg border border-neutral-300 dark:border-neutral-700 bg-white/80 dark:bg-neutral-900/80 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium mb-1 text-neutral-800 dark:text-neutral-200">
|
||
Main Concern
|
||
</label>
|
||
<select
|
||
name="concern"
|
||
value={form.concern}
|
||
onChange={handleChange}
|
||
className="w-full rounded-lg border border-neutral-300 dark:border-neutral-700 bg-white/80 dark:bg-neutral-900/80 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
>
|
||
<option value="">Select one</option>
|
||
<option value="speed">Speed / Performance</option>
|
||
<option value="security">Security</option>
|
||
<option value="dns">DNS / Email Issues</option>
|
||
<option value="hosting">Hosting Migration</option>
|
||
<option value="other">Other</option>
|
||
</select>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium mb-1 text-neutral-800 dark:text-neutral-200">
|
||
Message / Details
|
||
</label>
|
||
<textarea
|
||
name="message"
|
||
rows={4}
|
||
value={form.message}
|
||
onChange={handleChange}
|
||
placeholder="Describe your request..."
|
||
className="w-full rounded-lg border border-neutral-300 dark:border-neutral-700 bg-white/80 dark:bg-neutral-900/80 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mt-8 flex justify-between items-center">
|
||
<button
|
||
type="button"
|
||
onClick={() => setStep(1)}
|
||
className="text-sm text-neutral-600 dark:text-neutral-400 hover:underline"
|
||
>
|
||
← Back
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
disabled={loading}
|
||
className="rounded-lg bg-gradient-to-r from-blue-600 via-purple-600 to-blue-600 text-white font-medium px-6 py-2 hover:opacity-90 transition"
|
||
>
|
||
{loading ? "Submitting..." : "Submit Request"}
|
||
</button>
|
||
</div>
|
||
</motion.form>
|
||
)}
|
||
</AnimatePresence>
|
||
</section>
|
||
</main>
|
||
);
|
||
}
|