Seed: saas template (34 files)

This commit is contained in:
2026-07-23 22:08:29 +00:00
parent cf59c252e2
commit d8fe74c0db
+156
View File
@@ -0,0 +1,156 @@
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Mail, Phone, MapPin, Send, CheckCircle2 } from "lucide-react";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { useSaasMutation } from "@/hooks/use-tygarun";
export default function ContactPage() {
const { mutate: createLead, loading: sending } = useSaasMutation("crm", "/leads");
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [company, setCompany] = useState("");
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");
const [success, setSuccess] = useState(false);
return (
<div className="min-h-screen bg-white text-slate-900 antialiased">
<Header />
{/* ── HERO ── */}
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-50 to-white">
<div className="mx-auto max-w-7xl px-6 text-center">
<Badge className="mb-4 bg-indigo-50 text-indigo-600 border-0">Contact</Badge>
<h1 className="animate-fade-up text-4xl font-bold tracking-tight sm:text-5xl">Get in touch</h1>
<p className="mt-4 text-lg text-slate-500 max-w-2xl mx-auto">
Have questions? We would love to hear from you. Our team typically responds within 24 hours.
</p>
</div>
</section>
{/* ── CONTACT SPLIT ── */}
<section className="py-24">
<div className="mx-auto max-w-7xl px-6">
<div className="grid lg:grid-cols-5 gap-12">
{/* Left:Info */}
<div className="lg:col-span-2 space-y-8">
<div>
<h2 className="animate-fade-up text-3xl font-bold mb-4 tracking-tight">Contact Information</h2>
<p className="text-slate-500">Reach out through any of these channels and we will get back to you as soon as possible.</p>
</div>
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="h-10 w-10 rounded-xl bg-indigo-50 flex items-center justify-center flex-shrink-0">
<Mail className="h-5 w-5 text-indigo-600" />
</div>
<div>
<h3 className="font-semibold text-sm">Email</h3>
<p className="text-slate-500 text-sm mt-0.5">hello@nexus.dev</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="h-10 w-10 rounded-xl bg-indigo-50 flex items-center justify-center flex-shrink-0">
<Phone className="h-5 w-5 text-indigo-600" />
</div>
<div>
<h3 className="font-semibold text-sm">Phone</h3>
<p className="text-slate-500 text-sm mt-0.5">+1 (415) 555-0132</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="h-10 w-10 rounded-xl bg-indigo-50 flex items-center justify-center flex-shrink-0">
<MapPin className="h-5 w-5 text-indigo-600" />
</div>
<div>
<h3 className="font-semibold text-sm">Office</h3>
<p className="text-slate-500 text-sm mt-0.5">100 Market St, Suite 400, San Francisco, CA 94105</p>
</div>
</div>
</div>
</div>
{/* Right:Form */}
<div className="lg:col-span-3">
<Card className="border-slate-200 shadow-lg">
<CardContent className="p-8">
{success && (
<div className="mb-6 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-3 text-sm text-green-700">
<CheckCircle2 className="h-4 w-4 flex-shrink-0" /> Message sent successfully! We will get back to you within 24 hours.
</div>
)}
<form onSubmit={async (e) => { e.preventDefault(); try { await createLead({ name, email, company, subject, message }); setSuccess(true); toast.success("Message sent! Our team will reach out shortly."); } catch (err) { toast.error("Something went wrong. Please try again."); } }}>
<div className="grid sm:grid-cols-2 gap-4">
<div>
<Label htmlFor="contact-name" className="text-sm">Name</Label>
<Input id="contact-name" required placeholder="Your name" value={name} onChange={(e) => setName(e.target.value)} className="mt-1.5" />
</div>
<div>
<Label htmlFor="contact-email" className="text-sm">Email</Label>
<Input id="contact-email" type="email" required placeholder="you@company.com" value={email} onChange={(e) => setEmail(e.target.value)} className="mt-1.5" />
</div>
</div>
<div className="mt-4">
<Label htmlFor="contact-company" className="text-sm">Company</Label>
<Input id="contact-company" placeholder="Your company" value={company} onChange={(e) => setCompany(e.target.value)} className="mt-1.5" />
</div>
<div className="mt-4">
<Label htmlFor="contact-subject" className="text-sm">Subject</Label>
<Select value={subject} onValueChange={setSubject}>
<SelectTrigger className="mt-1.5">
<SelectValue placeholder="Select a subject" />
</SelectTrigger>
<SelectContent>
<SelectItem value="general">General Inquiry</SelectItem>
<SelectItem value="sales">Sales</SelectItem>
<SelectItem value="support">Technical Support</SelectItem>
<SelectItem value="partnership">Partnership</SelectItem>
<SelectItem value="press">Press & Media</SelectItem>
</SelectContent>
</Select>
</div>
<div className="mt-4">
<Label htmlFor="contact-message" className="text-sm">Message</Label>
<Textarea
id="contact-message"
required
placeholder="Tell us how we can help..."
value={message}
onChange={(e) => setMessage(e.target.value)}
className="mt-1.5 min-h-[120px]"
/>
</div>
<Button type="submit" disabled={sending} className="active:scale-[0.98] mt-6 w-full bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg font-semibold">
<Send className="mr-2 h-4 w-4" /> {sending ? "Sending..." : "Send Message"}
</Button>
</form>
</CardContent>
</Card>
</div>
</div>
</div>
</section>
{/* ── MAP PLACEHOLDER ── */}
<section className="pb-24">
<div className="mx-auto max-w-7xl px-6">
<div className="animate-scale-in h-64 rounded-2xl bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center relative overflow-hidden">
<div className="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxnIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iMC4wMyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiLz48L2c+PC9nPjwvc3ZnPg==')] opacity-60" />
<div className="absolute -top-10 -right-10 w-40 h-40 rounded-full bg-indigo-200/30 blur-3xl" />
<div className="absolute -bottom-10 -left-10 w-32 h-32 rounded-full bg-cyan-200/30 blur-3xl" />
<span className="relative text-slate-400 text-sm">Map Placeholder</span>
</div>
</div>
</section>
<Footer />
</div>
);
}