frontend: added Experience, Interests, and projects section

This commit is contained in:
2025-01-06 21:28:48 +02:00
parent bd6539d4b6
commit 72cc387b34
10 changed files with 337 additions and 42 deletions

View File

@ -21,6 +21,7 @@ FROM nginx:alpine
# Copy the built files from the build stage to the Nginx web root
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80

View File

@ -6,13 +6,18 @@ import Home from './pages/Home'
import Navbar from './components/Navbar'
import Footer from './components/Footer'
import Experience from './pages/Experience';
import Projects from './pages/Projects';
import Interests from './pages/Interests';
function App() {
const [darkMode, setDarkMode] = useState(true)
const [darkMode, setDarkMode] = useState(() => {
const savedMode = localStorage.getItem('DARK_MODE');
return savedMode ? JSON.parse(savedMode) : true;
});
const toggleDarkMode = (): void => {
console.log("Dark Mode")
setDarkMode(!darkMode)
}
return (
<>
<div className={darkMode ? "dark" : ""}>
@ -23,6 +28,8 @@ function App() {
<Routes>
<Route path='/' element={<Home/>} />
<Route path='/experience' element={<Experience/>} />
<Route path='/projects' element={<Projects/>} />
<Route path='/Interests' element={<Interests />} />
</Routes>
</Router>
<Footer />

View File

@ -1,7 +1,8 @@
import { Linkedin, Github, Award } from "lucide-react";
import { COLORS } from "../constants";
const Introduction = () => {
const BoldStyle = "text-blue-900 dark:text-blue-400 font-semibold";
const BoldStyle = "text-blue-900 dark:text-blue-300 font-semibold";
const socialLinks = [
{
@ -23,15 +24,15 @@ const Introduction = () => {
return (
<div className="text-center p-4 max-w-4xl mx-auto">
<h1 className="text-5xl text-blue-900 dark:text-blue-400 py-2 font-medium font-burtons">
<h1 className={`text-5xl py-2 ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} font-medium font-mono tracking-wide`}>
Taqi Tahmid
</h1>
<h2 className="text-2xl py-2 font-burtons dark:text-blue-200">
<h2 className={`text-2xl py-2 font-burtons ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>
Test Automation and DevOps Engineer
</h2>
<div className="text-md py-5 leading-8 text-gray-800 dark:text-gray-300 md:text-xl space-y-4">
<div className={`text-md py-5 ${COLORS.TEXT} ${COLORS.DARK_TEXT} leading-8 md:text-xl space-y-4`}>
<p>
I am a <span className={BoldStyle}>DevOps</span> and{" "}
<span className={BoldStyle}>Test Automation</span> engineer with a{" "}
@ -56,7 +57,7 @@ const Introduction = () => {
href={link.href}
target="_blank"
rel="noreferrer"
className="text-blue-900 dark:text-blue-400 hover:text-sky-600 dark:hover:text-sky-500 transition-colors duration-200"
className={`${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} hover:text-sky-600 dark:hover:text-sky-500 transition-colors duration-200`}
aria-label={link.label}
>
{link.icon}

View File

@ -1,5 +1,6 @@
import React, { useState, useRef } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { Menu, Sun, Moon, FileText, Mail, Check, Copy } from "lucide-react";
import { COLORS, EMAIL } from '../constants';
interface NavProps {
darkMode: boolean;
@ -9,12 +10,11 @@ interface NavProps {
const Navbar: React.FC<NavProps> = ({toggleDarkMode, darkMode}) => {
const [copied, setCopied] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const email = "taqitahmid@gmail.com";
const menuRef = useRef<HTMLDivElement>(null);
const handleCopyEmail = async () => {
try {
await navigator.clipboard.writeText(email);
await navigator.clipboard.writeText(EMAIL);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
@ -22,6 +22,10 @@ const Navbar: React.FC<NavProps> = ({toggleDarkMode, darkMode}) => {
}
};
useEffect(() => {
localStorage.setItem('DARK_MODE', String(darkMode))
},[darkMode])
const menuItem = [
{title: 'Home', href: '/'},
{title: 'Experience', href: '/experience'},
@ -31,14 +35,13 @@ const Navbar: React.FC<NavProps> = ({toggleDarkMode, darkMode}) => {
return (
<div className="w-full flex justify-center">
<nav className="py-5 mb-12 flex justify-between dark:text-white w-full max-w-5xl px-4">
<nav className="py-5 mb-6 flex justify-between dark:text-white w-full max-w-5xl px-4">
<button
onClick={handleCopyEmail}
className="flex items-center space-x-2 hover:bg-gray-100 dark:hover:bg-gray-800 px-3 py-2 rounded-lg transition-colors duration-200 group relative"
>
<Mail size={20} className="text-blue-500" />
<span className="hidden sm:inline">{email}</span>
<span className="sm:hidden">Email</span>
<Mail size={25} className={`${COLORS.DARK_PRIMARY}`} />
<span>Email</span>
{copied ? (
<Check size={16} className="text-green-500" />
) : (
@ -52,8 +55,8 @@ const Navbar: React.FC<NavProps> = ({toggleDarkMode, darkMode}) => {
<ul className="flex items-center">
<li className="transition ease-in-out delay-50 duration-100 cursor-pointer"
onClick={toggleDarkMode}>
<div className="bg-blue-900 hover:bg-blue-1000 dark:bg-blue-500 dark:hover:bg-blue-600 text-white p-2 rounded-lg ml-8 shadow-sm group relative">
{darkMode ? <Sun size={24} /> : <Moon size={24} />}
<div className="group relative">
{darkMode ? <Sun className='text-amber-400 hover:scale-110' size={30} /> : <Moon size={24} />}
<span className="pointer-events-none absolute -bottom-8 left-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-slate-800 px-2 py-1 text-xs text-slate-100 opacity-0 transition before:absolute before:left-1/2 before:top-full before:-translate-x-1/2 before:border-4 before:border-transparent before:border-t-slate-800 before:content-[''] group-hover:opacity-100">
{darkMode ? "Light Mode": "Dark Mode"}
</span>
@ -61,11 +64,11 @@ const Navbar: React.FC<NavProps> = ({toggleDarkMode, darkMode}) => {
</li>
<li className="transition ease-in-out delay-50 duration-100">
<div className="group relative">
<a className="bg-blue-900 hover:bg-blue-1000 dark:bg-blue-500 dark:hover:bg-blue-600 text-white p-2 rounded-lg ml-8 shadow-sm inline-flex"
<a className="text-white p-2 ml-8 inline-flex"
href="https://www.linkedin.com/in/taqi-tahmid/details/featured/1735981754176/single-media-viewer/?profileId=ACoAACDU_GsBCgKtvw2bmzbVwTy2WixBG6-e3JM"
target="_blank"
rel="noreferrer">
<FileText size={24} />
<FileText className="hover:scale-110 text-gray-800 dark:text-white" size={30} />
</a>
<span className="pointer-events-none absolute -bottom-8 left-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-slate-800 px-2 py-1 text-xs text-slate-100 opacity-0 transition before:absolute before:left-1/2 before:top-full before:-translate-x-1/2 before:border-4 before:border-transparent before:border-t-slate-800 before:content-[''] group-hover:opacity-100">
Resume

View File

@ -10,6 +10,7 @@ import dockerIcon from "../assets/docker-icon.svg";
import kubernetesIcon from "../assets/kubernetes.svg";
import prometheusIcon from "../assets/prometheus.svg";
import grafanaIcon from "../assets/grafana.svg";
import { COLORS } from "../constants";
const Skills = () => {
const skills = [
@ -29,10 +30,10 @@ const Skills = () => {
return (
<div>
<h1 className="text-2xl py-5 font-burtons dark:text-blue-200">
<h1 className={`text-2xl py-5 font-burtons ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>
Tools and Languages
</h1>
<div className="grid grid-cols-4 gap-6 text-md py-5 leading-8 text-gray-800 dark:text-gray-300 mx-auto max-w-2xl md:text-xl">
<div className={`grid grid-cols-4 gap-6 text-md py-5 leading-8 ${COLORS.TEXT} ${COLORS.DARK_TEXT} mx-auto max-w-2xl md:text-xl`}>
{skills.map((skill) => (
<div key={skill.name} className="flex flex-col items-center">
<img src={skill.icon} alt={skill.name} className="h-10 w-10" />

View File

@ -0,0 +1,10 @@
export const COLORS = {
PRIMARY: 'text-blue-900',
DARK_PRIMARY: 'dark:text-amber-50',
TEXT: 'text-gray-700',
DARK_TEXT: 'dark:text-gray-300',
BORDER: 'border-blue-900',
DARK_BORDER: 'border-blue-900',
}
export const EMAIL = 'taqitahmid@gmail.com'

View File

@ -1,8 +1,7 @@
import { Building2, Calendar, GraduationCap, MapPin, Microscope, Wrench } from 'lucide-react';
import { COLORS } from '../constants';
const Experience = () => {
const BoldStyle = "text-blue-900 dark:text-blue-400 font-semibold";
const experiences = [
{
title: "Experienced Developer (DevOps)",
@ -66,19 +65,19 @@ const Experience = () => {
return (
<div className="p-4 max-w-4xl mx-auto">
<h2 className="text-3xl text-blue-900 dark:text-blue-400 py-4 font-medium font-burtons text-center">
<h2 className={`text-3xl ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} py-4 font-medium font-burtons text-center`}>
Experience
</h2>
<div className="space-y-8">
{experiences.map((exp, index) => (
<div key={index} className="border-l-4 border-blue-900 dark:border-blue-400 pl-4 space-y-2">
<div key={index} className={`border-l-4 ${COLORS.BORDER} ${COLORS.DARK_BORDER} pl-4 space-y-2`}>
<div className="flex items-center gap-2">
<Building2 className="text-blue-900 dark:text-blue-400" size={24} />
<h3 className={`text-xl ${BoldStyle}`}>{exp.title}</h3>
<Building2 className={`${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`} size={24} />
<h3 className={`${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} text-xl font-semibold`}>{exp.title}</h3>
</div>
<div className="flex flex-col md:flex-row md:items-center gap-2 md:gap-6 text-gray-600 dark:text-gray-400">
<div className={`flex flex-col md:flex-row md:items-center gap-2 md:gap-6 ${COLORS.TEXT} ${COLORS.DARK_TEXT}`}>
<div className="flex items-center gap-1">
<Building2 size={16} />
<span>{exp.company}</span>
@ -93,16 +92,16 @@ const Experience = () => {
</div>
</div>
<div className="text-gray-800 dark:text-gray-300 space-y-1">
<div className={`${COLORS.TEXT} ${COLORS.DARK_TEXT} space-y-1`}>
{exp.responsibilities.map((resp, idx) => (
<p key={idx} className="flex items-start">
<span className="mr-2 mt-2 ml-4"></span>
<span>{resp}</span>
<p key={idx} className="flex text-wrap items-start">
<span className="mr-2 mt-1 ml-4">*</span>
<span className='text-left'>{resp}</span>
</p>
))}
</div>
<div className="text-gray-800 dark:text-gray-300 space-y-1 flex items-start">
<div className='flex items-center gap-2'>
<div className={`${COLORS.TEXT} ${COLORS.DARK_TEXT} space-y-1 flex items-start`}>
<div className='flex text-wrap items-center gap-2'>
<Wrench size={20}/>
<span>{exp.tools}</span>
</div>
@ -111,19 +110,19 @@ const Experience = () => {
))}
</div>
<h2 className="text-3xl text-blue-900 dark:text-blue-400 py-4 mt-8 font-medium font-burtons text-center">
<h2 className={`text-3xl ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} mt-6 py-4 font-medium font-burtons text-center`}>
Education
</h2>
<div className="space-y-6">
{education.map((edu, index) => (
<div key={index} className="border-l-4 border-blue-900 dark:border-blue-400 pl-4 space-y-2">
<div key={index} className={`border-l-4 ${COLORS.BORDER} ${COLORS.DARK_BORDER} pl-4 space-y-2`}>
<div className="flex items-center gap-2">
<GraduationCap className="text-blue-900 dark:text-blue-400" size={24} />
<h3 className={`text-xl ${BoldStyle}`}>{edu.degree}</h3>
<GraduationCap className={`${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`} size={24} />
<h3 className={`text-xl font-semibold ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>{edu.degree}</h3>
</div>
<div className="flex flex-col md:flex-row md:items-center gap-2 md:gap-6 text-gray-600 dark:text-gray-400">
<div className={`flex flex-col md:flex-row md:items-center gap-2 md:gap-6 ${COLORS.TEXT} ${COLORS.DARK_TEXT}`}>
<div className="flex items-center gap-1">
<Building2 size={16} />
<span>{edu.institution}</span>
@ -137,10 +136,10 @@ const Experience = () => {
<span>{edu.period}</span>
</div>
</div>
<div className="text-gray-600 dark:text-gray-400 space-y-1 flex items-start">
<div className='flex items-center gap-1'>
<Microscope />
<span>{edu.thesis}</span>
<div className={`space-y-1 flex items-start ${COLORS.TEXT} ${COLORS.DARK_TEXT}`}>
<div className='flex items-center gap-2'>
<Microscope size={20}/>
<span className={`text-left ${COLORS.TEXT} ${COLORS.DARK_TEXT}`}>{edu.thesis}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,110 @@
import { Camera, Plane, Film, Server, Cpu, Trophy, Car, Gamepad2 } from "lucide-react";
import { COLORS } from "../constants";
const Interests = () => {
const interests = [
{
title: "Travelling",
icon: <Plane size={32} />,
description: "Exploring new places, experiencing different cultures, and creating lasting memories through adventures around the world. From scenic landscapes to bustling cities, every journey is an opportunity to learn and grow."
},
{
title: "Photography",
icon: <Camera size={32} />,
description: "Capturing moments and perspectives through the lens. Particularly interested in landscape and street photography, always looking to improve composition skills and trying new techniques."
},
{
title: "Movies & Shows",
icon: <Film size={32} />,
description: "Passionate about cinema across various genres and cultures. Enjoy analyzing cinematography, storytelling techniques, and discovering hidden gems from different parts of the world."
},
{
title: "Homelab",
icon: <Server size={32} />,
description: "Managing a personal homelab setup for experimenting with self-hosted services, networking configurations, and learning about system administration in a hands-on environment."
},
{
title: "New Technologies",
icon: <Cpu size={32} />,
description: "Keeping up with the latest technological advancements, particularly in cloud computing, automation, and emerging DevOps tools. Enjoy experimenting with new frameworks and platforms."
},
{
title: "Playing Video Games",
icon: <Gamepad2 size={32} />,
description: "Enthusiastic gamer with a deep appreciation for interactive storytelling and virtual worlds. Enjoy exploring diverse genres from immersive RPGs to strategic multiplayer games."
},
{
title: "Sports",
icon: <Trophy size={32} />,
description: "Avid sports enthusiast following multiple disciplines:",
subInterests: [
{
name: "Football",
details: "Following major leagues and international tournaments, appreciating the tactical aspects and team dynamics of the beautiful game."
},
{
name: "Cricket",
details: "Enjoying both test matches and limited-overs formats, following international competitions and analyzing game strategies."
},
{
name: "Formula 1",
icon: <Car size={24} />,
details: "Following the high-speed world of F1, keeping up with team developments, race strategies, and technical innovations in motorsport."
}
]
}
];
return (
<div className="p-4 max-w-4xl mx-auto">
<h1 className={`text-4xl text-center py-2 mb-8 ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} font-medium font-mono tracking-wide`}>
Interests & Hobbies
</h1>
<div className="grid gap-6 md:grid-cols-2">
{interests.map((interest, index) => (
<div
key={index}
className={`group p-6 rounded-lg border-2 border-gray-300 dark:border-gray-700 hover:shadow-lg transition-all duration-300 ${interest.subInterests ? 'md:col-span-2' : ''}`}
>
<div className="flex items-center gap-4 mb-4">
<div className={`${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} group-hover:text-sky-600 dark:group-hover:text-sky-500 transition-colors duration-200`}>
{interest.icon}
</div>
<h2 className={`text-2xl ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>
{interest.title}
</h2>
</div>
<p className={`${COLORS.TEXT} ${COLORS.DARK_TEXT} leading-7`}>
{interest.description}
</p>
{interest.subInterests && (
<div className="mt-4 grid gap-4 md:grid-cols-3">
{interest.subInterests.map((subInterest, subIndex) => (
<div key={subIndex} className="p-4 bg-orange-100 dark:bg-gray-800 rounded-lg">
<h3 className={`text-lg font-semibold mb-2 ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>
{subInterest.name}
</h3>
<p className={`${COLORS.TEXT} ${COLORS.DARK_TEXT} text-sm leading-6`}>
{subInterest.details}
</p>
</div>
))}
</div>
)}
</div>
))}
</div>
<div className={`mt-8 text-center ${COLORS.TEXT} ${COLORS.DARK_TEXT}`}>
<p className="text-lg">
These interests not only provide a creative outlet and personal enjoyment but also complement my professional growth in technology and engineering.
</p>
</div>
</div>
);
};
export default Interests;

View File

@ -0,0 +1,121 @@
import { Github, Link } from "lucide-react";
import { COLORS } from "../constants";
const Projects = () => {
const BoldStyle = "text-blue-900 dark:text-blue-300 font-semibold";
const projects = [
{
title: "Self-Hosted Kubernetes Homelab Cluster",
description: "Architected and deployed a production-grade Kubernetes cluster in my homelab environment, showcasing infrastructure-as-code principles and modern DevOps practices. The cluster hosts a diverse ecosystem of services including my portfolio website, high-availability database, private container image registry, network-wide ad-blocking solution using AdGuard, and a comprehensive media server stack.",
technologies: ["Kubernetes", "KVM", "Linux", "Go", "Docker", "Helm", "MetalLB"],
links: [
{
icon: <Github size={24} />,
href: "https://github.com/yourusername/k8s-monitoring",
label: "GitHub"
}
]
},
{
title: "Personal Portfolio Website",
description: "Engineered and deployed a modern, responsive portfolio website using React, showcasing professional experience and technical projects. The website features a clean, intuitive design with dark/light theme support, responsive layouts, and smooth transitions. The site utilizes React's latest features and Tailwind CSS for styling. The entire application is containerized using Docker and deployed on a personal Kubernetes cluster, demonstrating a full DevOps pipeline from development to production.",
technologies: [
"React",
"Tailwind CSS",
"Docker",
"Kubernetes",
"Nginx"
],
links: [
{
icon: <Github size={24} />,
href: "https://github.com/TheTaqiTahmid/my-portfolio",
label: "GitHub"
},
{
icon: <Link size={24} />,
href: "https://portfolio.tahmidcloud.com",
label: "Live Demo"
}
]
},
{
title: "Automated Crypto Trading Bot",
description: "Engineered a Python-based cryptocurrency trading bot that interfaces with Binance's API to execute automated trades based on custom strategies. The bot implements real-time price monitoring, configurable stop-loss and take-profit mechanisms, and intelligent position management. It features a robust risk management system, detailed trade logging, and performance analytics. The bot can handle multiple trading pairs simultaneously.",
technologies: ["Python", "Binance API", "Pandas", "NumPy"],
links: [
{
icon: <Github size={24} />,
href: "https://github.com/TheTaqiTahmid/binanceCryptoBot",
label: "GitHub"
}
]
},
{
title: "Command Line Todo Application",
description: "Developed a fast and efficient CLI-based Todo application in Go that enables seamless task management through simple terminal commands. The application leverages Go's strong concurrency features. Users can perform CRUD operations (Create, Read, Update, Delete) on tasks.",
technologies: ["Go", "Cobra CLI"],
links: [
{
icon: <Github size={24} />,
href: "https://github.com/TheTaqiTahmid/todo",
label: "GitHub"
}
]
}
];
return (
<div className="p-4 max-w-4xl mx-auto">
<h1 className={`text-4xl text-center py-2 mb-8 ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} font-medium font-mono tracking-wide`}>
Projects
</h1>
<div className="space-y-8">
{projects.map((project, index) => (
<div
key={index}
className="border-2 border-gray-300 dark:border-gray-700 rounded-lg p-6 hover:shadow-lg transition-shadow duration-300"
>
<h2 className={`text-2xl mb-2 ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>
{project.title}
</h2>
<p className={`mb-4 ${COLORS.TEXT} ${COLORS.DARK_TEXT} leading-7`}>
{project.description}
</p>
<div className="mb-4">
<span className={BoldStyle}>Technologies: </span>
<span className={`${COLORS.TEXT} ${COLORS.DARK_TEXT}`}>
{project.technologies.join(", ")}
</span>
</div>
<div className="flex gap-4">
{project.links.map((link, linkIndex) => (
<div key={linkIndex} className="group relative">
<a
href={link.href}
target="_blank"
rel="noreferrer"
className={`${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} hover:text-sky-600 dark:hover:text-sky-500 transition-colors duration-200`}
aria-label={link.label}
>
{link.icon}
</a>
<span className="pointer-events-none absolute -top-8 left-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-slate-800 px-2 py-1 text-sm text-slate-100 opacity-0 transition before:absolute before:left-1/2 before:top-full before:-translate-x-1/2 before:border-4 before:border-transparent before:border-t-slate-800 before:content-[''] group-hover:opacity-100">
{link.label}
</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
);
};
export default Projects;

42
nginx.conf Normal file
View File

@ -0,0 +1,42 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
# Handle all specified routes
location / {
try_files $uri $uri/ /index.html;
}
location /home {
try_files $uri $uri/ /index.html;
}
location /experience {
try_files $uri $uri/ /index.html;
}
location /interest {
try_files $uri $uri/ /index.html;
}
location /project {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location /static/ {
expires 1y;
add_header Cache-Control "public, no-transform";
}
# Deny access to . files
location ~ /\. {
deny all;
}
}