aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorPedro Machado <machadop1407@gmail.com>2025-06-23 16:50:43 -0700
committerPedro Machado <machadop1407@gmail.com>2025-06-23 16:50:43 -0700
commiteb0eed40d780ccd81d8e0fe956e288c1724f1883 (patch)
tree8df03b483e3701e99f9df527723c977766b8315a /src/components
first commit
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Contact.jsx151
-rw-r--r--src/components/Hero.jsx145
-rw-r--r--src/components/Navbar.jsx63
-rw-r--r--src/components/Projects.jsx116
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>
+ );
+};