diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/Contact.jsx | 151 | ||||
-rw-r--r-- | src/components/Hero.jsx | 145 | ||||
-rw-r--r-- | src/components/Navbar.jsx | 63 | ||||
-rw-r--r-- | src/components/Projects.jsx | 116 |
4 files changed, 475 insertions, 0 deletions
diff --git a/src/components/Contact.jsx b/src/components/Contact.jsx new file mode 100644 index 0000000..af982e1 --- /dev/null +++ b/src/components/Contact.jsx @@ -0,0 +1,151 @@ +import { motion } from "framer-motion"; +import emailjs from "@emailjs/browser"; +import { useState } from "react"; + +const fadeInUp = { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.6 }, +}; + +const staggerContainer = { + animate: { + transition: { + staggerChildren: 0.1, + }, + }, +}; + +export const Contact = () => { + const [formData, setFormData] = useState({ + name: "", + email: "", + message: "", + }); + + const [formStatus, setFormStatus] = useState({ + submitting: false, + success: false, + error: false, + message: "", + }); + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + setFormStatus({ + submitting: true, + success: false, + error: false, + message: "", + }); + + try { + await emailjs.send( + import.meta.env.VITE_EMAILJS_SERVICE_ID, + import.meta.env.VITE_EMAILJS_TEMPLATE_ID, + { + name: formData.name, + email: formData.email, + message: formData.message, + } + ); + + setFormStatus({ + submitting: false, + success: true, + error: false, + message: "Message sent successfully!", + }); + + setFormData({ + name: "", + email: "", + message: "", + }); + } catch (error) { + setFormStatus({ + submitting: false, + success: false, + error: true, + message: "Failed to send message. Please try again.", + }); + } + }; + + return ( + <motion.section + id="contact" + className="contact" + initial={{ opacity: 0 }} + whileInView={{ opacity: 1 }} + viewport={{ once: true }} + transition={{ duration: 0.6 }} + > + <motion.h2 + variants={fadeInUp} + initial="initial" + animate="animate" + viewport={{ once: true }} + > + Get in Touch + </motion.h2> + + <motion.div className="contact-content" variants={fadeInUp}> + <motion.form className="contact-form" onSubmit={handleSubmit}> + <motion.input + type="text" + name="name" + placeholder="Your Name..." + required + whileFocus={{ scale: 1.02 }} + onChange={handleInputChange} + /> + <motion.input + type="email" + name="email" + placeholder="Your Email..." + required + whileFocus={{ scale: 1.02 }} + onChange={handleInputChange} + /> + <motion.textarea + name="message" + placeholder="Your Message..." + required + whileFocus={{ scale: 1.02 }} + onChange={handleInputChange} + /> + + <motion.button + className="submit-btn" + type="submit" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + disabled={formStatus.submitting} + > + {formStatus.submitting ? "Sending..." : "Send Message"} + </motion.button> + + {formStatus.message && ( + <motion.div + className={`form-status ${ + formStatus.success ? "success" : "error" + } `} + > + {formStatus.message} + </motion.div> + )} + </motion.form> + </motion.div> + </motion.section> + ); +}; diff --git a/src/components/Hero.jsx b/src/components/Hero.jsx new file mode 100644 index 0000000..d3a3b71 --- /dev/null +++ b/src/components/Hero.jsx @@ -0,0 +1,145 @@ +import { motion } from "framer-motion"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; + +const fadeInUp = { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.6 }, +}; + +const staggerContainer = { + animate: { + transition: { + staggerChildren: 0.1, + }, + }, +}; + +export const Hero = () => { + return ( + <motion.section + id="home" + className="hero" + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + transition={{ duration: 0.8, delay: 0.2 }} + > + <div className="hero-container"> + <motion.div + className="hero-content" + variants={staggerContainer} + initial="initial" + animate="animate" + > + <motion.div className="hero-badge"> + <span> 👋 Hello, I'm </span> + </motion.div> + <motion.h1 + className="glitch" + variants={fadeInUp} + whileHover={{ scale: 1.02 }} + > + PedroTech + </motion.h1> + <motion.h2 className="hero-subtitle" variants={fadeInUp}> + {" "} + Creative Developer & Designer + </motion.h2> + <motion.p className="hero-description" variants={fadeInUp}> + I craft beautiful digital experiences that combine stunning design + with powerful functionality. Specializing in modern web applications + and interactive user interfaces. + </motion.p> + + <motion.div className="cta-buttons" variants={staggerContainer}> + <motion.a + href="#projects" + className="cta-primary" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {" "} + View My Work + </motion.a> + <motion.a + href="#contacts" + className="cta-secondary" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + Contact Me + </motion.a> + </motion.div> + <motion.div className="social-links" variants={staggerContainer}> + <motion.a href="https://github.com" target="_blank"> + <i className="fab fa-github"> </i> + </motion.a> + <motion.a href="https://linkedin.com" target="_blank"> + <i className="fab fa-linkedin"> </i> + </motion.a> + <motion.a href="https://twitter.com" target="_blank"> + <i className="fab fa-twitter"> </i> + </motion.a> + </motion.div> + </motion.div> + + <motion.div + className="hero-image-container" + initial={{ opacity: 0, x: 50 }} + animate={{ opacity: 1, x: 0 }} + transition={{ duration: 0.8, delay: 0.4 }} + > + <div className="code-display"> + <SyntaxHighlighter + language="typescript" + customStyle={{ + margin: 0, + padding: "2rem", + height: "100%", + borderRadius: "20px", + background: "rgba(30, 41, 59, 0.8)", + backdropFilter: "blur(10px)", + marginBottom: 50, + }} + style={vscDarkPlus} + > + {`const aboutMe: DeveloperProfile = { + codename: "PedroTech", + origin: "🌍 Somewhere between a coffee shop and a terminal", + role: "Fullstack Web Sorcerer", + stack: { + languages: ["JavaScript", "TypeScript", "SQL"], + frameworks: ["React", "Next.js", "TailwindCSS", "Supabase"], + }, + traits: [ + "pixel-perfectionist", + "API whisperer", + "dark mode advocate", + "terminal aesthetic enthusiast", + ], + missionStatement: + "Turning ideas into interfaces and bugs into feature", + availability: "Available for hire", +};`} + </SyntaxHighlighter> + </div> + + <motion.div + className="floating-card" + animate={{ y: [0, -10, 0], rotate: [0, 2, 0] }} + transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }} + > + <div className="card-content"> + <span className="card-icon"> 💻 </span> + <span className="card-text"> + {" "} + Currently working on something awesome! + </span> + </div> + </motion.div> + </motion.div> + </div> + </motion.section> + ); +}; diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx new file mode 100644 index 0000000..8cb0e78 --- /dev/null +++ b/src/components/Navbar.jsx @@ -0,0 +1,63 @@ +import { motion } from "framer-motion"; + +const fadeInUp = { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.6 }, +}; + +const staggerContainer = { + animate: { + transition: { + staggerChildren: 0.1, + }, + }, +}; + +export const Navbar = () => { + return ( + <motion.nav + className="navbar" + initial={{ y: -100 }} + animate={{ y: 0 }} + transition={{ duration: 0.6, ease: "easeOut" }} + > + <motion.div + className="logo" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + Portfolio + </motion.div> + + <motion.ul + className="nav-links" + variants={staggerContainer} + initial="initial" + animate="animate" + > + <motion.li + variants={fadeInUp} + whileHover={{ scale: 1.1 }} + whileTap={{ scale: 0.95 }} + > + <a href="#home"> Home</a> + </motion.li> + <motion.li + variants={fadeInUp} + whileHover={{ scale: 1.1 }} + whileTap={{ scale: 0.95 }} + > + <a href="#projects"> Projects</a> + </motion.li> + <motion.li + variants={fadeInUp} + whileHover={{ scale: 1.1 }} + whileTap={{ scale: 0.95 }} + > + <a href="#contact"> Contact</a> + </motion.li> + </motion.ul> + </motion.nav> + ); +}; diff --git a/src/components/Projects.jsx b/src/components/Projects.jsx new file mode 100644 index 0000000..2696c48 --- /dev/null +++ b/src/components/Projects.jsx @@ -0,0 +1,116 @@ +import { motion } from "framer-motion"; + +const fadeInUp = { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.6 }, +}; + +const staggerContainer = { + animate: { + transition: { + staggerChildren: 0.1, + }, + }, +}; + +export const Projects = () => { + return ( + <motion.section + id="projects" + className="projects" + initial={{ opacity: 0 }} + whileInView={{ opacity: 1 }} + viewport={{ once: true }} + transition={{ duration: 0.6 }} + > + <motion.h2 + variants={fadeInUp} + initial="initial" + whileInView="animate" + viewport={{ once: true }} + > + My Projects + </motion.h2> + <motion.div + className="project-grid" + variants={staggerContainer} + initial="initial" + whileInView="animate" + viewport={{ once: true }} + > + <motion.div + className="project-card" + variants={fadeInUp} + whileHover={{ y: -10, transition: { duration: 0.2 } }} + > + <motion.div + className="project-image" + style={{ backgroundImage: "url('/projects/ai-saas.png')" }} + whileHover={{ scale: 1.05, transition: { duration: 0.2 } }} + /> + <h3> AI SaaS Platform</h3> + <p> + A modern SaaS platform built with Next.js and OpenAI integration, + featuring real-time AI-powered content generation and analytics. + </p> + <div className="project-tech"> + <span>Next.js</span> + <span>OpenAI</span> + <span>TailwindCSS</span> + </div> + </motion.div> + + <motion.div + className="project-card" + variants={fadeInUp} + whileHover={{ y: -10, transition: { duration: 0.2 } }} + > + <motion.div + className="project-image" + style={{ + backgroundImage: "url('/projects/social-media.png')", + }} + whileHover={{ scale: 1.05 }} + transition={{ duration: 0.2 }} + /> + <h3>Social Media Dashboard</h3> + <p> + A comprehensive social media management dashboard with analytics, + scheduling, and engagement tracking features. + </p> + <div className="project-tech"> + <span>React</span> + <span>Node.js</span> + <span>MongoDB</span> + </div> + </motion.div> + + <motion.div + className="project-card" + variants={fadeInUp} + whileHover={{ y: -10, transition: { duration: 0.2 } }} + > + <motion.div + className="project-image" + style={{ + backgroundImage: "url('/projects/stopwatch.png')", + }} + whileHover={{ scale: 1.05 }} + transition={{ duration: 0.2 }} + /> + <h3>Productivity Timer</h3> + <p> + A sleek productivity timer application with customizable work + sessions, statistics tracking, and dark mode support. + </p> + <div className="project-tech"> + <span>React</span> + <span>TypeScript</span> + <span>TailwindCSS</span> + </div> + </motion.div> + </motion.div> + </motion.section> + ); +}; |