first commit
This commit is contained in:
158
app/(public)/contact/_components/ContactForm.tsx
Normal file
158
app/(public)/contact/_components/ContactForm.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
"use client"
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
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 {
|
||||
Send,
|
||||
CheckCircle} from 'lucide-react';
|
||||
|
||||
|
||||
|
||||
// Contact Form Component
|
||||
const ContactForm: React.FC = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
message: ''
|
||||
});
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log('Form submitted:', formData);
|
||||
setSubmitted(true);
|
||||
setTimeout(() => setSubmitted(false), 3000);
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="max-w-3xl mx-auto mb-20"
|
||||
>
|
||||
<Card className="bg-white border-gray-200 shadow-xl">
|
||||
<CardContent className="p-8 md:p-12">
|
||||
<div className="text-center mb-8">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
||||
Send us a message
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
Fill out the form below and we'll get back to you within 24 hours
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{submitted ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="text-center py-12"
|
||||
>
|
||||
<div className="w-20 h-20 mx-auto mb-6 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<CheckCircle className="w-10 h-10 text-green-600" />
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-2">Thank you!</h3>
|
||||
<p className="text-gray-600">Your message has been sent successfully.</p>
|
||||
</motion.div>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Full Name *
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
placeholder="John Doe"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Email Address *
|
||||
</label>
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
placeholder="john@example.com"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Phone Number *
|
||||
</label>
|
||||
<Input
|
||||
type="tel"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleChange}
|
||||
placeholder="+1 (555) 000-0000"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Address
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
name="address"
|
||||
value={formData.address}
|
||||
onChange={handleChange}
|
||||
placeholder="City, Country"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Message *
|
||||
</label>
|
||||
<Textarea
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
placeholder="Tell us how we can help you..."
|
||||
rows={6}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
size="lg"
|
||||
className="w-full bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white"
|
||||
>
|
||||
Send Message
|
||||
<Send className="ml-2 w-5 h-5" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm
|
||||
46
app/(public)/contact/_components/GetInTouch.tsx
Normal file
46
app/(public)/contact/_components/GetInTouch.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
"use client"
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { CONTACT_DATA } from '@/services/Constants';
|
||||
|
||||
|
||||
|
||||
// Get In Touch Component
|
||||
const GetInTouch: React.FC = () => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-gray-50 rounded-3xl p-8 md:p-12 mb-20"
|
||||
>
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
||||
{CONTACT_DATA.getInTouch.title}
|
||||
</h2>
|
||||
<p className="text-gray-600 max-w-2xl mx-auto">
|
||||
{CONTACT_DATA.getInTouch.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{CONTACT_DATA.getInTouch.methods.map((method, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="text-center"
|
||||
>
|
||||
<div className={`w-16 h-16 mx-auto mb-4 bg-white rounded-2xl shadow-md flex items-center justify-center ${method.color}`}>
|
||||
<method.icon className="w-8 h-8" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">{method.title}</h3>
|
||||
<p className="text-gray-600 text-sm mb-2">{method.description}</p>
|
||||
<p className={`font-semibold ${method.color}`}>{method.contact}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default GetInTouch
|
||||
51
app/(public)/contact/_components/GlobalOffices.tsx
Normal file
51
app/(public)/contact/_components/GlobalOffices.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
"use client"
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {MapPin} from 'lucide-react';
|
||||
import { CONTACT_DATA } from '@/services/Constants';
|
||||
|
||||
|
||||
|
||||
// Global Offices Component
|
||||
const GlobalOffices: React.FC = () => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-20"
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4">
|
||||
{CONTACT_DATA.offices.title}
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-12">{CONTACT_DATA.offices.description}</p>
|
||||
<div className="grid md:grid-cols-2 gap-8 max-w-4xl mx-auto">
|
||||
{CONTACT_DATA.offices.locations.map((location, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.2 }}
|
||||
whileHover={{ y: -5 }}
|
||||
>
|
||||
<Card className="bg-white border-gray-200 hover:shadow-lg transition-all duration-300">
|
||||
<CardContent className="p-8 text-center">
|
||||
<div className="text-6xl mb-4">{location.icon}</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-4">{location.city}</h3>
|
||||
<div className="flex items-start justify-center gap-2 text-gray-600">
|
||||
<MapPin className="w-5 h-5 flex-shrink-0 mt-1 text-blue-600" />
|
||||
<div>
|
||||
<p>{location.address}</p>
|
||||
<p>{location.address2}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default GlobalOffices
|
||||
73
app/(public)/contact/_components/HappyCustomers.tsx
Normal file
73
app/(public)/contact/_components/HappyCustomers.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
"use client"
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import {
|
||||
Users,
|
||||
Star
|
||||
} from 'lucide-react';
|
||||
import { CONTACT_DATA } from '@/services/Constants';
|
||||
|
||||
|
||||
// Happy Customers Component
|
||||
const HappyCustomers: React.FC = () => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-gradient-to-br from-purple-50 via-blue-50 to-pink-50 rounded-3xl p-8 md:p-12"
|
||||
>
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="grid lg:grid-cols-2 gap-8 items-center">
|
||||
<div>
|
||||
<Badge className="mb-4 px-4 py-2 bg-yellow-100 text-yellow-800 border-yellow-300">
|
||||
<Star className="w-4 h-4 mr-2 inline fill-yellow-500" />
|
||||
{CONTACT_DATA.customers.badge}
|
||||
</Badge>
|
||||
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-gray-900 mb-6">
|
||||
{CONTACT_DATA.customers.title}
|
||||
<span className="text-orange-600">{CONTACT_DATA.customers.highlight}</span>
|
||||
</h2>
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700 text-white"
|
||||
>
|
||||
Try for FREE
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-4 mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="w-8 h-8 text-purple-600" />
|
||||
<div>
|
||||
<p className="text-3xl font-bold text-gray-900">{CONTACT_DATA.customers.count}</p>
|
||||
<p className="text-sm text-gray-600">{CONTACT_DATA.customers.label}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{CONTACT_DATA.customers.logos.map((logo, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
>
|
||||
<div className="bg-white rounded-xl p-4 shadow-md hover:shadow-lg transition-all duration-300 flex items-center justify-center h-20">
|
||||
<span className={`font-bold text-lg ${logo.color}`}>{logo.name}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default HappyCustomers
|
||||
31
app/(public)/contact/_components/HeroSection.tsx
Normal file
31
app/(public)/contact/_components/HeroSection.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CONTACT_DATA } from "@/services/Constants";
|
||||
|
||||
const HeroSection: React.FC = () => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="text-center mb-16 mt-10"
|
||||
>
|
||||
<Badge className="mb-4 px-4 py-2 bg-purple-100 text-purple-700">
|
||||
{CONTACT_DATA.hero.badge}
|
||||
</Badge>
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-gray-900 mb-6">
|
||||
{CONTACT_DATA.hero.title}{" "}
|
||||
<span className="bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">
|
||||
us
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-lg md:text-xl text-gray-600 mb-2 max-w-3xl mx-auto">
|
||||
{CONTACT_DATA.hero.description}
|
||||
</p>
|
||||
<p className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
{CONTACT_DATA.hero.subtitle}
|
||||
</p>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default HeroSection;
|
||||
43
app/(public)/contact/_components/QuickActions.tsx
Normal file
43
app/(public)/contact/_components/QuickActions.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client"
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { CONTACT_DATA } from '@/services/Constants';
|
||||
|
||||
// Quick Actions Component
|
||||
const QuickActions: React.FC = () => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="grid md:grid-cols-2 gap-8 mb-20"
|
||||
>
|
||||
{CONTACT_DATA.quickActions.map((action, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.2 }}
|
||||
>
|
||||
<Card className="bg-white border-gray-200 hover:shadow-lg transition-all duration-300 h-full">
|
||||
<CardContent className="p-8 text-center">
|
||||
<div className={`w-16 h-16 mx-auto mb-6 bg-gradient-to-br ${action.color} rounded-2xl flex items-center justify-center`}>
|
||||
<action.icon className="w-8 h-8 text-purple-600" />
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">{action.title}</h3>
|
||||
<p className="text-gray-600 mb-6 leading-relaxed">
|
||||
{action.description}
|
||||
</p>
|
||||
<Button variant="outline" className="border-purple-300 text-purple-600 hover:bg-purple-50">
|
||||
{action.cta}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default QuickActions
|
||||
Reference in New Issue
Block a user