Merge version_23_1777235961560 into main #20
@@ -8,6 +8,7 @@ import TeamTestimonialsPage from "@/pages/TeamTestimonialsPage";
|
||||
import PricingPage from "@/pages/PricingPage";
|
||||
import PricingSimpleCards from "@/components/sections/pricing/PricingSimpleCards";
|
||||
import { FaqSection } from "@/components/sections/faq/FaqSection";
|
||||
import ContactForm from "@/components/sections/contact/ContactForm";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
@@ -56,6 +57,14 @@ export default function App() {
|
||||
<div id="faq" data-section="faq">
|
||||
<FaqSection />
|
||||
</div>
|
||||
<div id="contact" data-section="contact">
|
||||
<ContactForm
|
||||
tag="Contact Us"
|
||||
title="Get in touch"
|
||||
description="We'd love to hear from you. Fill out the form below and we'll get back to you as soon as possible."
|
||||
buttonText="Send Message"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
71
src/components/sections/contact/ContactForm.tsx
Normal file
71
src/components/sections/contact/ContactForm.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { motion } from "motion/react";
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import Input from "@/components/ui/Input";
|
||||
import Textarea from "@/components/ui/Textarea";
|
||||
|
||||
const ContactForm = ({
|
||||
tag,
|
||||
title,
|
||||
description,
|
||||
buttonText,
|
||||
}: {
|
||||
tag: string;
|
||||
title: string;
|
||||
description: string;
|
||||
buttonText: string;
|
||||
}) => {
|
||||
return (
|
||||
<section aria-label="Contact section" className="py-20">
|
||||
<div className="w-content-width mx-auto">
|
||||
<div className="flex flex-col items-center gap-3 md:gap-2 mb-10">
|
||||
<span className="px-3 py-1 text-sm card rounded">{tag}</span>
|
||||
<TextAnimation
|
||||
text={title}
|
||||
variant="slide-up"
|
||||
tag="h2"
|
||||
className="text-4xl md:text-5xl font-medium text-center text-balance"
|
||||
/>
|
||||
<TextAnimation
|
||||
text={description}
|
||||
variant="slide-up"
|
||||
tag="p"
|
||||
className="md:max-w-6/10 text-lg leading-tight text-center"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-15%" }}
|
||||
transition={{ duration: 0.6, ease: "easeOut" }}
|
||||
className="max-w-2xl mx-auto card rounded p-6 md:p-10"
|
||||
>
|
||||
<form className="flex flex-col gap-5" onSubmit={(e) => e.preventDefault()}>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="text-sm font-medium">Name</label>
|
||||
<Input id="name" placeholder="Your name" required />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="email" className="text-sm font-medium">Email</label>
|
||||
<Input id="email" type="email" placeholder="you@example.com" required />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="subject" className="text-sm font-medium">Subject</label>
|
||||
<Input id="subject" placeholder="How can we help?" required />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="message" className="text-sm font-medium">Message</label>
|
||||
<Textarea id="message" placeholder="Your message..." rows={5} required />
|
||||
</div>
|
||||
<Button text={buttonText} variant="primary" type="submit" className="w-full mt-2" />
|
||||
</form>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
@@ -13,9 +13,10 @@ interface ButtonProps {
|
||||
animateImmediately?: boolean;
|
||||
delay?: number;
|
||||
className?: string;
|
||||
type?: "button" | "submit" | "reset";
|
||||
}
|
||||
|
||||
const Button = ({ text, variant = "primary", href, onClick, animate = false, animateImmediately = false, delay = 0, className = "" }: ButtonProps) => {
|
||||
const Button = ({ text, variant = "primary", href, onClick, animate = false, animateImmediately = false, delay = 0, className = "", type = "button" }: ButtonProps) => {
|
||||
const handleClick = useButtonClick(href, onClick);
|
||||
|
||||
const classes = cls(
|
||||
@@ -26,7 +27,7 @@ const Button = ({ text, variant = "primary", href, onClick, animate = false, ani
|
||||
|
||||
const button = href
|
||||
? <a href={href} onClick={handleClick} className={classes}>{text}</a>
|
||||
: <button onClick={handleClick} className={classes}>{text}</button>;
|
||||
: <button type={type} onClick={handleClick} className={classes}>{text}</button>;
|
||||
|
||||
if (animateImmediately) {
|
||||
return (
|
||||
@@ -54,4 +55,4 @@ const Button = ({ text, variant = "primary", href, onClick, animate = false, ani
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
export default Button;
|
||||
18
src/components/ui/Input.tsx
Normal file
18
src/components/ui/Input.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { InputHTMLAttributes } from "react";
|
||||
import { cls } from "@/lib/utils";
|
||||
|
||||
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = ({ className, ...props }: InputProps) => {
|
||||
return (
|
||||
<input
|
||||
className={cls(
|
||||
"flex h-10 w-full rounded border border-foreground/20 bg-card px-3 py-2 text-sm placeholder:text-foreground/50 focus:outline-none focus:ring-2 focus:ring-primary-cta focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
18
src/components/ui/Textarea.tsx
Normal file
18
src/components/ui/Textarea.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { TextareaHTMLAttributes } from "react";
|
||||
import { cls } from "@/lib/utils";
|
||||
|
||||
interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
const Textarea = ({ className, ...props }: TextareaProps) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cls(
|
||||
"flex min-h-[80px] w-full rounded border border-foreground/20 bg-card px-3 py-2 text-sm placeholder:text-foreground/50 focus:outline-none focus:ring-2 focus:ring-primary-cta focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Textarea;
|
||||
Reference in New Issue
Block a user