-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: optimize Contact component with form submission functionality a…
…nd animations
- Loading branch information
Showing
2 changed files
with
171 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,132 @@ | ||
import React from 'react'; | ||
import { useRef, useState } from 'react'; | ||
|
||
import emailjs from '@emailjs/browser'; | ||
import { motion } from 'framer-motion'; | ||
|
||
import EarthCanvas from '@/components/canvas/Earth'; | ||
import SectionWrapper from '@/hoc/SectionWrapper'; | ||
import { styles } from '@/styles'; | ||
import { slideIn } from '@/utils/motion'; | ||
|
||
const Contact = () => { | ||
return <div>Contact</div>; | ||
const formRef = useRef(); | ||
const [form, setForm] = useState({ | ||
name: '', | ||
email: '', | ||
message: '', | ||
}); | ||
|
||
const [loading, setLoading] = useState(false); | ||
|
||
const handleChange = (e) => { | ||
const { target } = e; | ||
const { name, value } = target; | ||
|
||
setForm({ | ||
...form, | ||
[name]: value, | ||
}); | ||
}; | ||
|
||
const handleSubmit = (e) => { | ||
e.preventDefault(); | ||
setLoading(true); | ||
|
||
emailjs | ||
.send( | ||
import.meta.env.VITE_APP_EMAILJS_SERVICE_ID, | ||
import.meta.env.VITE_APP_EMAILJS_TEMPLATE_ID, | ||
{ | ||
from_name: form.name, | ||
to_name: 'JavaScript Mastery', | ||
from_email: form.email, | ||
to_email: 'sujata@jsmastery.pro', | ||
message: form.message, | ||
}, | ||
import.meta.env.VITE_APP_EMAILJS_PUBLIC_KEY | ||
) | ||
.then( | ||
() => { | ||
setLoading(false); | ||
alert('Thank you. I will get back to you as soon as possible.'); | ||
|
||
setForm({ | ||
name: '', | ||
email: '', | ||
message: '', | ||
}); | ||
}, | ||
(error) => { | ||
setLoading(false); | ||
console.error(error); | ||
|
||
alert('Ahh, something went wrong. Please try again.'); | ||
} | ||
); | ||
}; | ||
return ( | ||
<div className={`xl:mt-12 flex xl:flex-row flex-col-reverse gap-10 overflow-hidden`}> | ||
<motion.div | ||
variants={slideIn('left', 'tween', 0.2, 1)} | ||
className="flex-[0.75] bg-black-100 p-8 rounded-2xl" | ||
> | ||
<p className={styles.sectionSubText}>Get in touch</p> | ||
<h3 className={styles.sectionHeadText}>Contact.</h3> | ||
|
||
<form ref={formRef} onSubmit={handleSubmit} className="mt-12 flex flex-col gap-8"> | ||
<label className="flex flex-col"> | ||
<span className="text-white font-medium mb-4">Your Name</span> | ||
<input | ||
type="text" | ||
name="name" | ||
value={form.name} | ||
onChange={handleChange} | ||
placeholder="What's your good name?" | ||
className="bg-tertiary py-4 px-6 placeholder:text-secondary text-white rounded-lg outline-none border-none font-medium" | ||
/> | ||
</label> | ||
<label className="flex flex-col"> | ||
<span className="text-white font-medium mb-4">Your email</span> | ||
<input | ||
type="email" | ||
name="email" | ||
value={form.email} | ||
onChange={handleChange} | ||
placeholder="What's your web address?" | ||
className="bg-tertiary py-4 px-6 placeholder:text-secondary text-white rounded-lg outline-none border-none font-medium" | ||
/> | ||
</label> | ||
<label className="flex flex-col"> | ||
<span className="text-white font-medium mb-4">Your Message</span> | ||
<textarea | ||
rows={7} | ||
name="message" | ||
value={form.message} | ||
onChange={handleChange} | ||
placeholder="What you want to say?" | ||
className="bg-tertiary py-4 px-6 placeholder:text-secondary text-white rounded-lg outline-none border-none font-medium" | ||
/> | ||
</label> | ||
|
||
<button | ||
type="submit" | ||
className="bg-tertiary py-3 px-8 rounded-xl outline-none w-fit text-white font-bold shadow-md shadow-primary" | ||
> | ||
{loading ? 'Sending...' : 'Send'} | ||
</button> | ||
</form> | ||
</motion.div> | ||
|
||
<motion.div | ||
variants={slideIn('right', 'tween', 0.2, 1)} | ||
className="xl:flex-1 xl:h-auto md:h-[550px] h-[350px]" | ||
> | ||
<EarthCanvas /> | ||
</motion.div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Contact; | ||
const WrappedContact = SectionWrapper(Contact, 'contact'); | ||
|
||
export default WrappedContact; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Suspense } from 'react'; | ||
|
||
import { OrbitControls, Preload, useGLTF } from '@react-three/drei'; | ||
import { Canvas } from '@react-three/fiber'; | ||
|
||
import CanvasLoader from '@/components/Loader'; | ||
|
||
const Earth = () => { | ||
const earth = useGLTF('./planet/scene.gltf'); | ||
|
||
return <primitive object={earth.scene} scale={2.5} position-y={0} rotation-y={0} />; | ||
}; | ||
|
||
const EarthCanvas = () => { | ||
return ( | ||
<Canvas | ||
shadows | ||
frameloop="demand" | ||
dpr={[1, 2]} | ||
gl={{ preserveDrawingBuffer: true }} | ||
camera={{ | ||
fov: 45, | ||
near: 0.1, | ||
far: 200, | ||
position: [-4, 3, 6], | ||
}} | ||
> | ||
<Suspense fallback={<CanvasLoader />}> | ||
<OrbitControls | ||
autoRotate | ||
enableZoom={false} | ||
maxPolarAngle={Math.PI / 2} | ||
minPolarAngle={Math.PI / 2} | ||
/> | ||
<Earth /> | ||
|
||
<Preload all /> | ||
</Suspense> | ||
</Canvas> | ||
); | ||
}; | ||
|
||
export default EarthCanvas; |