diff --git a/.gitignore b/.gitignore index 4d29575..44c92ca 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,8 @@ # production /build - +# sensitive files +/src/components/apikey.js # misc .DS_Store .env.local diff --git a/package-lock.json b/package-lock.json index 4cdf8de..ddb86f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "bootstrap": "^5.3.3", + "emailjs-com": "^3.2.0", "react": "^18.3.1", "react-bootstrap": "^2.10.4", "react-bootstrap-icons": "^1.11.4", @@ -7299,6 +7300,15 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.822.tgz", "integrity": "sha512-qJzHIt4dRRFKjHHvaExCrG95F65kUP3xysaEZ4I2+/R/uIyr5Ar5g/rkAnrRz0parRUYwzpqN8Pz1HgoiYQPpg==" }, + "node_modules/emailjs-com": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/emailjs-com/-/emailjs-com-3.2.0.tgz", + "integrity": "sha512-Prbz3E1usiAwGjMNYRv6EsJ5c373cX7/AGnZQwOfrpNJrygQJ15+E9OOq4pU8yC977Z5xMetRfc3WmDX6RcjAA==", + "deprecated": "The SDK name changed to @emailjs/browser", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -23616,6 +23626,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.822.tgz", "integrity": "sha512-qJzHIt4dRRFKjHHvaExCrG95F65kUP3xysaEZ4I2+/R/uIyr5Ar5g/rkAnrRz0parRUYwzpqN8Pz1HgoiYQPpg==" }, + "emailjs-com": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/emailjs-com/-/emailjs-com-3.2.0.tgz", + "integrity": "sha512-Prbz3E1usiAwGjMNYRv6EsJ5c373cX7/AGnZQwOfrpNJrygQJ15+E9OOq4pU8yC977Z5xMetRfc3WmDX6RcjAA==" + }, "emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", diff --git a/package.json b/package.json index f0e37a0..7d4ea1d 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "bootstrap": "^5.3.3", + "emailjs-com": "^3.2.0", "react": "^18.3.1", "react-bootstrap": "^2.10.4", "react-bootstrap-icons": "^1.11.4", diff --git a/src/App.css b/src/App.css index 459e738..b876fb6 100644 --- a/src/App.css +++ b/src/App.css @@ -358,3 +358,28 @@ nav.navbar .navbar-toggler[aria-expanded="true"] .navbar-toggler-icon { background-size: cover; padding-top: 150px; /* Same as the padding top of the banner */ } + +/* contactform */ + +.modal-header { + background-color: #121212; + color: white; +} + +.modal-body { + background-color: #f8f9fa; +} + +.modal-footer { + background-color: #121212; +} + +.btn-primary { + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:hover { + background-color: #0056b3; + border-color: #0056b3; +} diff --git a/src/components/ContactForm.js b/src/components/ContactForm.js index e69de29..d034d93 100644 --- a/src/components/ContactForm.js +++ b/src/components/ContactForm.js @@ -0,0 +1,104 @@ +// src/components/ContactForm.js +import React, { useState } from 'react'; +import { Modal, Button, Form } from 'react-bootstrap'; +import emailjs from 'emailjs-com'; +import emailConfig from './apikey'; +const ContactForm = ({ show, handleClose }) => { + const [formData, setFormData] = useState({ + firstName: '', + lastName: '', + email: '', + message: '' + }); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData({ + ...formData, + [name]: value + }); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + + emailjs.send( + emailConfig.serviceId, // EmailJS service ID + emailConfig.templateId, // EmailJS template ID + formData, + emailConfig.userId // EmailJS user ID + ) + .then((result) => { + alert('Message sent successfully!'); + handleClose(); + }, (error) => { + alert('Failed to send message, please try again.'); + }); + }; + + return ( + <Modal show={show} onHide={handleClose}> + <Modal.Header closeButton> + <Modal.Title>Contact Me</Modal.Title> + </Modal.Header> + <Modal.Body> + <Form onSubmit={handleSubmit}> + <Form.Group controlId="formFirstName"> + <Form.Label>First Name</Form.Label> + <Form.Control + type="text" + placeholder="Enter your first name" + name="firstName" + value={formData.firstName} + onChange={handleChange} + required + /> + </Form.Group> + + <Form.Group controlId="formLastName"> + <Form.Label>Last Name</Form.Label> + <Form.Control + type="text" + placeholder="Enter your last name" + name="lastName" + value={formData.lastName} + onChange={handleChange} + required + /> + </Form.Group> + + <Form.Group controlId="formEmail"> + <Form.Label>Email</Form.Label> + <Form.Control + type="email" + placeholder="Enter your email" + name="email" + value={formData.email} + onChange={handleChange} + required + /> + </Form.Group> + + <Form.Group controlId="formMessage"> + <Form.Label>Message</Form.Label> + <Form.Control + as="textarea" + rows={3} + placeholder="Enter your message" + name="message" + value={formData.message} + onChange={handleChange} + required + /> + </Form.Group> + + <Button variant="primary" type="submit"> + Send Message + </Button> + </Form> + </Modal.Body> + </Modal> + ); +}; + +export default ContactForm; diff --git a/src/components/Navbar.js b/src/components/Navbar.js index ab055ba..56997d1 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -1,13 +1,19 @@ -import React, { useState, useEffect } from "react"; -import { Container, Nav, Navbar } from "react-bootstrap"; -import logo from "../assets/img/logo.svg"; -import navIcon from "../assets/img/github.svg"; -import linkedin from "../assets/img/linkedln.svg" +// src/components/NavBar.js +import React, { useState, useEffect } from 'react'; +import { Container, Nav, Navbar } from 'react-bootstrap'; +import logo from '../assets/img/logo.svg'; +import navIcon from '../assets/img/github.svg'; +import linkedin from '../assets/img/linkedln.svg'; +import ContactForm from './ContactForm'; + const NavBar = () => { - const [activeLink, setActiveLink] = useState("home"); + const [activeLink, setActiveLink] = useState('home'); const [scrolled, setScrolled] = useState(false); + const [showContactForm, setShowContactForm] = useState(false); + + const handleShowContactForm = () => setShowContactForm(true); + const handleCloseContactForm = () => setShowContactForm(false); - // Check for scrolling useEffect(() => { const onScroll = () => { if (window.scrollY > 50) { @@ -17,10 +23,9 @@ const NavBar = () => { } }; - window.addEventListener("scroll", onScroll); + window.addEventListener('scroll', onScroll); - // Remove event listener when component unmounts - return () => window.removeEventListener("scroll", onScroll); + return () => window.removeEventListener('scroll', onScroll); }, []); const onUpdateLink = (value) => { @@ -28,7 +33,7 @@ const NavBar = () => { }; return ( - <Navbar expand="lg" className={scrolled ? "scrolled" : ""}> + <Navbar expand="lg" className={scrolled ? 'scrolled' : ''}> <Container> <Navbar.Brand href="#home"> <img src={logo} alt="Logo" /> @@ -39,18 +44,18 @@ const NavBar = () => { <Nav.Link href="#home" className={ - activeLink === "home" ? "active navbar-link" : "navbar-link" + activeLink === 'home' ? 'active navbar-link' : 'navbar-link' } - onClick={() => onUpdateLink("home")} + onClick={() => onUpdateLink('home')} > Home </Nav.Link> <Nav.Link href="#projects" className={ - activeLink === "projects" ? "active navbar-link" : "navbar-link" + activeLink === 'projects' ? 'active navbar-link' : 'navbar-link' } - onClick={() => onUpdateLink("projects")} + onClick={() => onUpdateLink('projects')} > Projects </Nav.Link> @@ -64,12 +69,13 @@ const NavBar = () => { <img src={linkedin} alt="linkedin Icon" /> </a> </div> - <button className="vvd" onClick={() => console.log("connect")}> + <button className="vvd" onClick={handleShowContactForm}> <span>Let's connect!</span> </button> </span> </Navbar.Collapse> </Container> + <ContactForm show={showContactForm} handleClose={handleCloseContactForm} /> </Navbar> ); };