Seed: portfolio template (18 files)
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
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 { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
||||||
|
import {
|
||||||
|
ArrowRight, Mail, MapPin, Linkedin, Github, Twitter, CheckCircle2
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useSaasMutation } from "@/hooks/use-tygarun";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
const faqs = [
|
||||||
|
{ q: "What is your typical project timeline?", a: "Most projects take 4-8 weeks depending on scope. I will provide a detailed timeline during our initial consultation." },
|
||||||
|
{ q: "What are your rates?", a: "I offer both project-based and retainer pricing. Rates depend on project scope and complexity. Let us chat to find the right fit." },
|
||||||
|
{ q: "Do you work with international clients?", a: "Absolutely! I work with clients worldwide and am comfortable with asynchronous communication across time zones." },
|
||||||
|
{ q: "What tools do you use?", a: "Figma for design, React/Next.js for development, and tools like Framer Motion, Tailwind CSS, and TypeScript for building modern interfaces." },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
const [sending, setSending] = useState(false);
|
||||||
|
// Contact enquiries flow to tyga.run CRM (seed success until crm is live)
|
||||||
|
const { mutate: createLead } = useSaasMutation("crm", "/leads");
|
||||||
|
|
||||||
|
const handleContact = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = e.currentTarget;
|
||||||
|
const fd = new FormData(form);
|
||||||
|
setSending(true);
|
||||||
|
try {
|
||||||
|
await createLead({
|
||||||
|
name: fd.get("name"),
|
||||||
|
email: fd.get("email"),
|
||||||
|
subject: fd.get("subject"),
|
||||||
|
message: fd.get("message"),
|
||||||
|
});
|
||||||
|
toast.success("Message sent! I'll get back to you within 24 hours.");
|
||||||
|
form.reset();
|
||||||
|
} catch (err) {
|
||||||
|
toast.error("Something went wrong. Please email me directly.");
|
||||||
|
} finally {
|
||||||
|
setSending(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-background text-foreground antialiased">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
<section className="pt-32 pb-24 px-6">
|
||||||
|
<div className="mx-auto max-w-6xl">
|
||||||
|
<h1 className="text-4xl sm:text-5xl font-bold tracking-tight mb-4 animate-fade-up">Let's Work Together</h1>
|
||||||
|
<p className="text-lg text-muted-foreground max-w-xl mb-16">Have a project in mind? I would love to hear about it. Send me a message and I will get back to you within 24 hours.</p>
|
||||||
|
|
||||||
|
<div className="grid gap-12 lg:grid-cols-2">
|
||||||
|
{/* ── LEFT: Contact info ── */}
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="space-y-6">
|
||||||
|
{[
|
||||||
|
{ icon: Mail, label: "Email", value: "hello@alexrivera.dev" },
|
||||||
|
{ icon: MapPin, label: "Location", value: "San Francisco, CA" },
|
||||||
|
{ icon: Linkedin, label: "LinkedIn", value: "linkedin.com/in/alexrivera" },
|
||||||
|
].map((item, i) => (
|
||||||
|
<div key={i} className="flex items-start gap-4">
|
||||||
|
<div className="h-12 w-12 rounded-xl bg-primary/10 flex items-center justify-center flex-shrink-0 shadow-lg shadow-primary/10">
|
||||||
|
<item.icon className="h-5 w-5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-medium">{item.label}</div>
|
||||||
|
<div className="text-muted-foreground">{item.value}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-3 pt-4">
|
||||||
|
{[Github, Linkedin, Twitter].map((Icon, i) => (
|
||||||
|
<a key={i} href="#" className="h-10 w-10 rounded-full bg-muted flex items-center justify-center text-muted-foreground hover:text-foreground hover:bg-muted/80 transition-colors">
|
||||||
|
<Icon className="h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── RIGHT: Form ── */}
|
||||||
|
<Card className="border shadow-sm backdrop-blur-sm bg-card/80 border-border/50">
|
||||||
|
<CardContent className="p-6">
|
||||||
|
<form onSubmit={handleContact} className="space-y-4">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium mb-1.5 block">Name</label>
|
||||||
|
<Input name="name" placeholder="Your name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium mb-1.5 block">Email</label>
|
||||||
|
<Input name="email" placeholder="your@email.com" type="email" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium mb-1.5 block">Subject</label>
|
||||||
|
<Input name="subject" placeholder="Project inquiry" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-medium mb-1.5 block">Message</label>
|
||||||
|
<Textarea name="message" placeholder="Tell me about your project..." className="min-h-[140px] resize-none" required />
|
||||||
|
</div>
|
||||||
|
<Button type="submit" disabled={sending} className="w-full rounded-full active:scale-[0.98]">
|
||||||
|
{sending ? "Sending..." : "Send Message"} <ArrowRight className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<div className="flex items-center justify-center gap-2 text-sm text-muted-foreground mt-3">
|
||||||
|
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
||||||
|
<span>Response time: within 24 hours</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ── FAQ ── */}
|
||||||
|
<section className="py-24 bg-muted/30">
|
||||||
|
<div className="mx-auto max-w-3xl px-6">
|
||||||
|
<h2 className="text-3xl sm:text-4xl font-bold tracking-tight text-center mb-12 animate-fade-up">Frequently Asked Questions</h2>
|
||||||
|
<Accordion type="single" collapsible className="w-full stagger">
|
||||||
|
{faqs.map((f, i) => (
|
||||||
|
<AccordionItem key={i} value={`faq-\${i}`}>
|
||||||
|
<AccordionTrigger className="text-left">{f.q}</AccordionTrigger>
|
||||||
|
<AccordionContent className="text-muted-foreground">{f.a}</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user