212 lines
7.8 KiB
TypeScript
212 lines
7.8 KiB
TypeScript
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
import { Input } from "@/components/ui/input";
|
||
|
|
import { Textarea } from "@/components/ui/textarea";
|
||
|
|
import { AnimatePresence, motion } from "framer-motion";
|
||
|
|
import { CheckCircle, Send, X } from "lucide-react";
|
||
|
|
import { useState } from "react";
|
||
|
|
|
||
|
|
interface ContactModalProps {
|
||
|
|
isOpen: boolean;
|
||
|
|
onClose: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function ContactModal({ isOpen, onClose }: ContactModalProps) {
|
||
|
|
const [formData, setFormData] = useState({
|
||
|
|
name: "",
|
||
|
|
email: "",
|
||
|
|
message: "",
|
||
|
|
});
|
||
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||
|
|
const [isSuccess, setIsSuccess] = useState(false);
|
||
|
|
|
||
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
||
|
|
e.preventDefault();
|
||
|
|
setIsSubmitting(true);
|
||
|
|
|
||
|
|
// Simulate form submission
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||
|
|
|
||
|
|
setIsSubmitting(false);
|
||
|
|
setIsSuccess(true);
|
||
|
|
|
||
|
|
// Reset after showing success
|
||
|
|
setTimeout(() => {
|
||
|
|
setIsSuccess(false);
|
||
|
|
setFormData({ name: "", email: "", message: "" });
|
||
|
|
onClose();
|
||
|
|
}, 2000);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleChange = (
|
||
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||
|
|
) => {
|
||
|
|
setFormData((prev) => ({
|
||
|
|
...prev,
|
||
|
|
[e.target.name]: e.target.value,
|
||
|
|
}));
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<AnimatePresence>
|
||
|
|
{isOpen && (
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
className="fixed inset-0 z-[100] flex items-center justify-center p-4"
|
||
|
|
>
|
||
|
|
{/* Backdrop */}
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
className="absolute inset-0 bg-background/80 backdrop-blur-sm"
|
||
|
|
onClick={onClose}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Modal */}
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, scale: 0.9, y: 20 }}
|
||
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||
|
|
exit={{ opacity: 0, scale: 0.9, y: 20 }}
|
||
|
|
transition={{ type: "spring", damping: 25, stiffness: 300 }}
|
||
|
|
className="relative w-full max-w-md rounded-3xl p-8 border-gradient overflow-hidden"
|
||
|
|
>
|
||
|
|
{/* Close button */}
|
||
|
|
<motion.button
|
||
|
|
onClick={onClose}
|
||
|
|
className="absolute top-4 right-4 p-2 rounded-full glass hover:bg-primary/20 transition-colors"
|
||
|
|
whileHover={{ scale: 1.1 }}
|
||
|
|
whileTap={{ scale: 0.9 }}
|
||
|
|
>
|
||
|
|
<X className="w-5 h-5" />
|
||
|
|
</motion.button>
|
||
|
|
|
||
|
|
{/* Content */}
|
||
|
|
<div className="relative z-10">
|
||
|
|
<AnimatePresence mode="wait">
|
||
|
|
{isSuccess ? (
|
||
|
|
<motion.div
|
||
|
|
key="success"
|
||
|
|
initial={{ opacity: 0, scale: 0.8 }}
|
||
|
|
animate={{ opacity: 1, scale: 1 }}
|
||
|
|
exit={{ opacity: 0, scale: 0.8 }}
|
||
|
|
className="text-center py-8"
|
||
|
|
>
|
||
|
|
<motion.div
|
||
|
|
initial={{ scale: 0 }}
|
||
|
|
animate={{ scale: 1 }}
|
||
|
|
transition={{
|
||
|
|
type: "spring",
|
||
|
|
damping: 15,
|
||
|
|
stiffness: 300,
|
||
|
|
delay: 0.2,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<CheckCircle className="w-20 h-20 text-primary mx-auto mb-4 neon-text-glow" />
|
||
|
|
</motion.div>
|
||
|
|
<h3 className="text-2xl font-bold mb-2">Message Sent!</h3>
|
||
|
|
<p className="text-muted-foreground">
|
||
|
|
We'll get back to you soon.
|
||
|
|
</p>
|
||
|
|
</motion.div>
|
||
|
|
) : (
|
||
|
|
<motion.div
|
||
|
|
key="form"
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
exit={{ opacity: 0 }}
|
||
|
|
>
|
||
|
|
<h2 className="text-2xl font-bold mb-2">Let's Talk</h2>
|
||
|
|
<p className="text-muted-foreground mb-6">
|
||
|
|
Ready to start your project? Tell us about it.
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 10 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
transition={{ delay: 0.1 }}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
name="name"
|
||
|
|
placeholder="Your Name"
|
||
|
|
value={formData.name}
|
||
|
|
onChange={handleChange}
|
||
|
|
required
|
||
|
|
className=" border focus:border-primary focus:neon-glow transition-all"
|
||
|
|
/>
|
||
|
|
</motion.div>
|
||
|
|
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 10 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
transition={{ delay: 0.2 }}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
name="email"
|
||
|
|
type="email"
|
||
|
|
placeholder="Your Email"
|
||
|
|
value={formData.email}
|
||
|
|
onChange={handleChange}
|
||
|
|
required
|
||
|
|
className=" border focus:border-primary focus:neon-glow transition-all"
|
||
|
|
/>
|
||
|
|
</motion.div>
|
||
|
|
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 10 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
transition={{ delay: 0.3 }}
|
||
|
|
>
|
||
|
|
<Textarea
|
||
|
|
name="message"
|
||
|
|
placeholder="Tell us about your project..."
|
||
|
|
value={formData.message}
|
||
|
|
onChange={handleChange}
|
||
|
|
required
|
||
|
|
rows={4}
|
||
|
|
className=" border focus:border-primary focus:neon-glow transition-all resize-none"
|
||
|
|
/>
|
||
|
|
</motion.div>
|
||
|
|
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 10 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
transition={{ delay: 0.4 }}
|
||
|
|
>
|
||
|
|
<Button
|
||
|
|
type="submit"
|
||
|
|
disabled={isSubmitting}
|
||
|
|
className="w-full bg-primary text-primary-foreground font-semibold py-3 rounded-full neon-glow hover:neon-glow-strong transition-all group"
|
||
|
|
>
|
||
|
|
{isSubmitting ? (
|
||
|
|
<motion.div
|
||
|
|
animate={{ rotate: 360 }}
|
||
|
|
transition={{
|
||
|
|
duration: 1,
|
||
|
|
repeat: Infinity,
|
||
|
|
ease: "linear",
|
||
|
|
}}
|
||
|
|
className="w-5 h-5 border-2 border-primary-foreground/30 border-t-primary-foreground rounded-full"
|
||
|
|
/>
|
||
|
|
) : (
|
||
|
|
<>
|
||
|
|
Send Message
|
||
|
|
<Send className="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" />
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</Button>
|
||
|
|
</motion.div>
|
||
|
|
</form>
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</motion.div>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
);
|
||
|
|
}
|