diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b6bfb5b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +build +.dockerignore +Dockerfile +.git +.gitignore +README.md +npm-debug.log 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/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a7f2adb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Step 1: Use a base image with Node.js +FROM node:16 AS build + +# Step 2: Set the working directory inside the container +WORKDIR /app + +# Step 3: Copy package.json and package-lock.json (or yarn.lock) +COPY package*.json ./ + +# Step 4: Install dependencies +RUN npm install + +# Step 5: Copy the rest of your application code +COPY . . + +# Step 6: Build the React application +RUN npm run build + +# Step 7: Use a base image to serve the application +FROM nginx:alpine + +# Step 8: Copy the build output from the previous stage to the Nginx public directory +COPY --from=build /app/build /usr/share/nginx/html + +# Step 9: Expose port 80 to the outside +EXPOSE 80 + +# Step 10: Start the Nginx server +CMD ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index 14306c3..199e5b3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,63 @@ +# My Portfolio -### `npm start` +This is a personal portfolio web application built using React. It showcases various projects and skills and includes a contact form for users to get in touch. -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. +## Table of Contents + +- [Getting Started](#getting-started) +- [Features](#features) +- [Installation](#installation) +- [Usage](#usage) +- [EmailJS Setup](#emailjs-setup) +- [Contributing](#contributing) +- [License](#license) + +## Getting Started + +To get a local copy of the project up and running, follow these simple steps. + +### Prerequisites + +Make sure you have the following installed: + +- [Node.js](https://nodejs.org/) (v14 or later) +- [npm](https://www.npmjs.com/) (comes with Node.js) +- [EmailJS](https://www.emailjs.com) + +Create an account on EmailJS, then create a new email service and template. Copy the Service ID, Template ID, and User ID. + +### EmailJS Configuration + +1. **Create a configuration file:** + + Create a file named `emailConfig.js` in the `src/components` directory with the following content: + + ```javascript + // src/components/emailConfig.js + + const emailConfig = { + serviceId: 'yourserviceid', // Replace with your EmailJS Service ID + templateId: 'yourtemplateid', // Replace with your EmailJS Template ID + userId: 'youruserid' // Replace with your EmailJS User ID + }; + + export default emailConfig; +you then import it to the ContactForm.js + +### Installation + +1. **Install the dependencies:** + + +bash + npm install + + +## Usage + +### Running the App + +To start the app in development mode: + +bash + npm start \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 86a5673..ddb86f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,12 @@ "@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", "react-dom": "^18.3.1", + "react-multi-carousel": "^2.8.5", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -7298,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", @@ -15176,6 +15187,14 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "node_modules/react-multi-carousel": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.5.tgz", + "integrity": "sha512-C5DAvJkfzR2JK9YixZ3oyF9x6R4LW6nzTpIXrl9Oujxi4uqP9SzVVCjl+JLM3tSdqdjAx/oWZK3dTVBSR73Q+w==", + "engines": { + "node": ">=8" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -23607,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", @@ -29066,6 +29090,11 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-multi-carousel": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.5.tgz", + "integrity": "sha512-C5DAvJkfzR2JK9YixZ3oyF9x6R4LW6nzTpIXrl9Oujxi4uqP9SzVVCjl+JLM3tSdqdjAx/oWZK3dTVBSR73Q+w==" + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", diff --git a/package.json b/package.json index e73cf98..7d4ea1d 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,12 @@ "@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", "react-dom": "^18.3.1", + "react-multi-carousel": "^2.8.5", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/src/App.css b/src/App.css index 11f535b..f4381fd 100644 --- a/src/App.css +++ b/src/App.css @@ -1,4 +1,4 @@ -/* custom fonts */ +/* Custom fonts */ @font-face { font-family: centra; src: url('./assets/font/CentraNo2-Bold.ttf'); @@ -15,32 +15,34 @@ font-weight: 400; } -/* default CSS */ +/* Default CSS */ +#root { + min-height: 100vh; +} * { margin: 0; padding: 0; box-sizing: border-box; -} +} -html { +html { scroll-behavior: smooth; scroll-padding-top: 75px; -} +} -body { +body { font-weight: 400; - overflow: hidden; position: relative; color: #685454 !important; font-family: 'Centra', sans-serif !important; -} -h1, h2, h3, h4, h5, h6 { - margin: 0; - padding: 0; - line-height: normal; -} +} +h1, h2, h3, h4, h5, h6 { + margin: 0; + padding: 0; + line-height: normal; +} p, a, li, button, ul { margin: 0; @@ -67,35 +69,36 @@ input:focus, textarea:focus, select:focus { outline: none; } -@media (min-width:1700px) { - main .container { - max-width: 100%; - padding: 0 150px; - } +@media (min-width: 1700px) { + main .container { + max-width: 100%; + padding: 0 150px; + } } p.success { - color: green; + color: green; } p.danger { - color: red; + color: red; } -/************ Navbar Css ************/ + +/* Navbar CSS */ nav.navbar { padding: 18px 0; position: fixed; width: 100%; top: 0; z-index: 9999; - transition: 0.32s ease-in-out; + transition: 0.32s ease-in-out; } nav.navbar.scrolled { padding: 0px 0; background-color: #121212; } nav.navbar a.navbar-brand { - width: 9%; + width: 9%; } nav.navbar .navbar-nav .nav-link.navbar-link { font-weight: 400; @@ -107,48 +110,48 @@ nav.navbar .navbar-nav .nav-link.navbar-link { } nav.navbar .navbar-nav a.nav-link.navbar-link:hover, nav.navbar .navbar-nav a.nav-link.navbar-link.active { - opacity: 1; + opacity: 1; } span.navbar-text { - display: flex; - align-items: center; + display: flex; + align-items: center; } .social-icon { - display: inline-block; - margin-left: 14px; + display: inline-block; + margin-left: 14px; } .social-icon a { - width: 42px; - height: 42px; - background: rgba(217, 217, 217, 0.1); - display: inline-flex; - border-radius: 50%; - margin-right: 6px; - align-items: center; - justify-content: center; - line-height: 1; - border: 1px solid rgba(255, 255, 255, 0.5); + width: 42px; + height: 42px; + background: rgba(217, 217, 217, 0.1); + display: inline-flex; + border-radius: 50%; + margin-right: 6px; + align-items: center; + justify-content: center; + line-height: 1; + border: 1px solid rgba(255, 255, 255, 0.5); } .social-icon a::before { - content: ""; - width: 42px; - height: 42px; - position: absolute; - background-color: #ffffff; - border-radius: 50%; - transform: scale(0); - transition: 0.3s ease-in-out; + content: ""; + width: 42px; + height: 42px; + position: absolute; + background-color: #ffffff; + border-radius: 50%; + transform: scale(0); + transition: 0.3s ease-in-out; } .social-icon a:hover::before { - transform: scale(1); + transform: scale(1); } .social-icon a img { - width: 40%; - z-index: 1; - transition: 0.3s ease-in-out; + width: 40%; + z-index: 1; + transition: 0.3s ease-in-out; } .social-icon a:hover img { - filter: brightness(0) saturate(100%) invert(0%) sepia(7%) saturate(98%) hue-rotate(346deg) brightness(95%) contrast(86%); + filter: brightness(0) saturate(100%) invert(0%) sepia(7%) saturate(98%) hue-rotate(346deg) brightness(95%) contrast(86%); } .navbar-text button { font-weight: 700; @@ -157,9 +160,9 @@ span.navbar-text { padding: 18px 34px; font-size: 18px; margin-left: 18px; - position: relative; - background-color: transparent; - transition: 0.3s ease-in-out; + position: relative; + background-color: transparent; + transition: 0.3s ease-in-out; } .navbar-text button span { z-index: 1; @@ -179,15 +182,15 @@ span.navbar-text { color: #121212; } .navbar-text button:hover::before { - content: ""; - width: 100%; - height: 100%; - position: absolute; + content: ""; + width: 100%; + height: 100%; + position: absolute; } nav.navbar .navbar-toggler:active, nav.navbar .navbar-toggler:focus { - outline: none; - box-shadow: none; + outline: none; + box-shadow: none; } nav.navbar .navbar-toggler-icon { width: 24px; @@ -199,43 +202,42 @@ nav.navbar .navbar-toggler-icon { top: -2px; } nav.navbar .navbar-toggler-icon:focus { - border-bottom: 2px solid #fff; + border-bottom: 2px solid #fff; } nav.navbar .navbar-toggler-icon:after, nav.navbar .navbar-toggler-icon:before { - width: 24px; - position: absolute; - height: 2px; - background-color: #fff; - top: 0; - left: 0; - content: ''; - z-index: 2; - transition: all 300ms linear; + width: 24px; + position: absolute; + height: 2px; + background-color: #fff; + top: 0; + left: 0; + content: ''; + z-index: 2; + transition: all 300ms linear; } nav.navbar .navbar-toggler-icon:after { - top: 8px; + top: 8px; } nav.navbar .navbar-toggler[aria-expanded="true"] .navbar-toggler-icon:after { - transform: rotate(45deg); - background-color: #fff; - height: 2px; + transform: rotate(45deg); + background-color: #fff; + height: 2px; } nav.navbar .navbar-toggler[aria-expanded="true"] .navbar-toggler-icon:before { - transform: translateY(8px) rotate(-45deg); - background-color: #fff; - height: 2px; + transform: translateY(8px) rotate(-45deg); + background-color: #fff; + height: 2px; } nav.navbar .navbar-toggler[aria-expanded="true"] .navbar-toggler-icon { - border-color: transparent; + border-color: transparent; } -/* banner */ +/* Banner */ .banner { margin-top: 0; - margin-bottom: 0; padding: 150px 0 100px 0; position: relative; - background-image: url('./assets/img/01.jpg'); + background-image: url('./assets/img/136646.jpg'); background-position: top center; background-size: cover; background-repeat: no-repeat; @@ -288,57 +290,101 @@ nav.navbar .navbar-toggler[aria-expanded="true"] .navbar-toggler-icon { animation: updown 3s linear infinite; } @keyframes updown { - 0% { - transform: translateY(-20px); - } - 50% { - transform: translateY(20px); - } - 100% { - transform: translateY(-20px); - } + 0% { + transform: translateY(-20px); + } + 50% { + transform: translateY(20px); + } + 100% { + transform: translateY(-20px); + } } .txt-rotate > .wrap { border-right: 0.08em solid #666; } +/* General styling for projects section */ +.projects-section { + padding: 0 0 50px 0; + position: relative; +} + +/* Styling for the carousel container */ +.skill-slider { + width: 80%; + margin: 0 auto; + position: relative; +} -.App { - text-align: center; +/* Styling for the cards within the carousel */ +.card-custom { + background-color: #463c3c; /* Light grey background for cards */ + border-radius: 20px; /* Rounded corners */ + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Shadow effect */ + overflow: hidden; /* Ensure content doesn't overflow */ + position: relative; /* Allow for overlapping */ + transition: transform 250ms, box-shadow 250ms; /* Smooth transitions */ } -.App-logo { - height: 40vmin; - pointer-events: none; +/* Image styling */ +.card-custom img { + width: 100%; + border-bottom: 1px solid #ddd; /* Divider line between image and text */ } -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } +/* Styling for card content */ +.card-custom-body { + padding: 1rem; } -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); +/* Hover effects for the card */ +.card-custom:hover { + transform: translateY(-10px) rotate(3deg); /* Move up and rotate */ + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* Enhance shadow on hover */ +} + +/* Styling to ensure cards overlap */ +.card-custom:nth-child(n+2) { + margin-left: -40px; /* Overlap effect */ +} + +/* Adjust carousel arrows */ +.react-multi-carousel-arrow { + background-color: #333; /* Dark background for arrows */ + border-radius: 50%; /* Round arrows */ + height: 30px; + width: 30px; + z-index: 10; +} + +.react-multi-carousel-arrow::before { + font-size: 20px; + color: #fff; /* White arrow color */ +} + + +/* contactform */ + +.modal-header { + background-color: #121212; color: white; } -.App-link { - color: #61dafb; +.modal-body { + background-color: #f8f9fa; } -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +.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/App.js b/src/App.js index 740ae14..8e4cfc2 100644 --- a/src/App.js +++ b/src/App.js @@ -1,13 +1,17 @@ import './App.css'; import NavBar from './components/Navbar'; import Banner from './components/banner'; +import Projects from './components/Projects'; import 'bootstrap/dist/css/bootstrap.min.css'; function App() { return ( -
C-R5z$^(){oA|Hl8zkdagT-^_ojWb~BeWE2dP4A-eYKVBWg
zt^j!f^bC(QhJ`*<$%WDbUM5IufoXx)M~_kQbe-J{!xY*&Ppw67*y_STn!Laa7k;rk
z+5(`jixxtK@4d
z# ;$q6uUL5^7{}o615Rx;}EWWNN
zG8VWVBN(}Tet{}nd4C#5-yML$f7dXtYz2_nzQwxp&ZcL~)#V9~H9vR_0Kzo^0I~cL
z9;t#rG}x;@rlYi&KXNCjabgeg>!Ct9L $x-VFA*ugg?u9H7m@tW){9e~nQmU{h~
zUsVq;n|7(eVs^EG^O|e1r*B3l<4C7O*%pKc2%vH7RFIOR){_--G;*-{eR?_u$
zJg$sT3ciunF8hGGw)ulL$LtM>p}WoBPJa`Ier%d3uNZBr*YB`!y3Q5V1s1xu5(E1k
zmgY;Xf>55LSup2pYl==t!NqQwx_8m)50&Ez(lhO}TbzH?(h9BCFXfbs(oDD(%(~=y
zM0?BntK`E6&76Wt9kN)L{$)#)7vq->en!R)RlS