Merge branch 'sidebar'

This commit is contained in:
2025-04-21 19:39:50 +03:00
11 changed files with 14465 additions and 327 deletions

14428
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,8 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-markdown": "^9.0.3", "react-markdown": "^9.0.3",
"react-router-dom": "^7.1.1" "react-router-dom": "^7.1.1",
"styled-component": "^2.8.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.9.0", "@eslint/js": "^9.9.0",

View File

@ -4,6 +4,7 @@ import { Routes, Route } from 'react-router-dom'
import './App.css' import './App.css'
import Home from './pages/Home' import Home from './pages/Home'
import Navbar from './components/Navbar' import Navbar from './components/Navbar'
import Sidebar from './components/Sidebar'
import Footer from './components/Footer' import Footer from './components/Footer'
import Experience from './pages/Experience' import Experience from './pages/Experience'
import Projects from './pages/Projects' import Projects from './pages/Projects'
@ -24,14 +25,22 @@ function App() {
<main className="bg-amber-50 px-10 dark:bg-gray-900"> <main className="bg-amber-50 px-10 dark:bg-gray-900">
<section className="min-h-screen"> <section className="min-h-screen">
<Navbar toggleDarkMode={toggleDarkMode} darkMode={darkMode} /> <Navbar toggleDarkMode={toggleDarkMode} darkMode={darkMode} />
<Router> <div className="md:flex md:flex-row md:h-full">
<Routes> <div className="mb-4 md:w-1/4 md:max-w-[260px] md:max-h-[900px] border-2 border-gray-300 dark:border-gray-700 rounded-lg shadow-sm">
<Route path="/" element={<Home />} /> <Sidebar />
<Route path="/experience" element={<Experience />} /> </div>
<Route path="/projects" element={<Projects />} /> <div className="md:flex-1">
<Route path="/Interests" element={<Interests />} /> <Router>
</Routes> <Routes>
</Router> <Route path="/" element={<Home />} />
<Route path="/experience" element={<Experience />} />
<Route path="/projects" element={<Projects />} />
<Route path="/Interests" element={<Interests />} />
</Routes>
</Router>
</div>
<div className="hidden md:block md:w-1/4 md:max-w-[260px] md:max-h-[900px]"></div>
</div>
<Footer /> <Footer />
</section> </section>
</main> </main>

View File

@ -29,7 +29,7 @@ const Footer = () => {
<div className="flex items-center gap-2 text-xs"> <div className="flex items-center gap-2 text-xs">
<Coffee size={14} className="inline-block" /> <Coffee size={14} className="inline-block" />
<span>Powered by coffee and countless hours of debugging</span> <span>Powered by coffee and Love</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,4 @@
import { COLORS, SOCIALLINKS } from '../constants' import { COLORS } from '../constants'
import { Tooltip } from './Tooltip'
const Introduction = () => { const Introduction = () => {
const BoldStyle = 'text-blue-900 dark:text-blue-300 font-semibold' const BoldStyle = 'text-blue-900 dark:text-blue-300 font-semibold'
@ -35,24 +34,6 @@ const Introduction = () => {
and infrastructure automation to implementing robust testing frameworks. and infrastructure automation to implementing robust testing frameworks.
</p> </p>
</div> </div>
<div className="flex justify-center gap-8 py-3">
{SOCIALLINKS.map((link, index) => (
<div key={index} className="group relative">
<Tooltip label={link.label} position="top">
<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>
</Tooltip>
</div>
))}
</div>
</div> </div>
) )
} }

View File

@ -0,0 +1,75 @@
import { COLORS, SOCIALLINKS } from '../constants'
import { Tooltip } from './Tooltip'
interface LinkProps {
href: string
children: React.ReactNode
icon: React.ReactNode
label: string
}
const Link: React.FC<LinkProps> = ({ icon, href, children, label }) => {
return (
<div className="transition-all duration-200 hover:translate-x-1">
<div className="m-2 border border-gray-200 dark:border-gray-700 p-2 md:p-3 rounded-lg hover:shadow-md">
<Tooltip label={label} position="top" additionalClass="md:hidden">
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center text-gray-700 dark:text-gray-200 hover:text-blue-600 dark:hover:text-blue-400"
>
{icon}
<span className="text-sm font-medium hidden md:inline">{children}</span>
</a>
</Tooltip>
</div>
</div>
)
}
const Sidebar = () => {
const SectionTitle = ({ children }: { children: React.ReactNode }) => (
<h2
className={`mt-4 mb-4 text-lg font-semibold ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} hidden md:block`}
>
{children}
</h2>
)
return (
<div
className={`grid grid-cols-4 md:grid-cols-1 p-2 md:p-6 max-w-xs mx-auto ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY} rounded-xl shadow-sm`}
>
<SectionTitle>Contact</SectionTitle>
{SOCIALLINKS.contact.map((link, index) => (
<Link key={index} icon={link.icon} href={link.href} label={link.text}>
{link.text}
</Link>
))}
<SectionTitle>Connect</SectionTitle>
{SOCIALLINKS.connect.map((link, index) => (
<Link key={index} icon={link.icon} href={link.href} label={link.text}>
{link.text}
</Link>
))}
<SectionTitle>Follow</SectionTitle>
{SOCIALLINKS.follow.map((link, index) => (
<Link key={index} icon={link.icon} href={link.href} label={link.text}>
{link.text}
</Link>
))}
<SectionTitle>Achievements</SectionTitle>
{SOCIALLINKS.publications.map((link, index) => (
<Link key={index} icon={link.icon} href={link.href} label={link.text}>
{link.text}
</Link>
))}
</div>
)
}
export default Sidebar

View File

@ -1,6 +1,33 @@
import { COLORS, SKILLS } from '../constants' import pythonIcon from '../assets/python.svg'
import robotIcon from '../assets/robotframework-svgrepo-com.svg'
import goIcon from '../assets/go-original.svg'
import reactIcon from '../assets/react.svg'
import ansibleIcon from '../assets/ansible.svg'
import terraformIcon from '../assets/terraform-icon.svg'
import jenkinsIcon from '../assets/jenkins.svg'
import gitIcon from '../assets/git-icon.svg'
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 = () => {
const skills = [
{ name: 'Python', icon: pythonIcon },
{ name: 'Golang', icon: goIcon },
{ name: 'React', icon: reactIcon },
{ name: 'Robot Framework', icon: robotIcon },
{ name: 'Ansible', icon: ansibleIcon },
{ name: 'Terraform', icon: terraformIcon },
{ name: 'Jenkins', icon: jenkinsIcon },
{ name: 'Git', icon: gitIcon },
{ name: 'Docker', icon: dockerIcon },
{ name: 'Kubernetes', icon: kubernetesIcon },
{ name: 'Prometheus', icon: prometheusIcon },
{ name: 'Grafana', icon: grafanaIcon },
]
return ( return (
<div> <div>
<h1 className={`text-2xl py-5 font-burtons ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}> <h1 className={`text-2xl py-5 font-burtons ${COLORS.PRIMARY} ${COLORS.DARK_PRIMARY}`}>
@ -9,7 +36,7 @@ const Skills = () => {
<div <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`} 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) => ( {skills.map((skill) => (
<div key={skill.name} className="flex flex-col items-center"> <div key={skill.name} className="flex flex-col items-center">
<img src={skill.icon} alt={skill.name} className="h-10 w-10" /> <img src={skill.icon} alt={skill.name} className="h-10 w-10" />
<p className="mt-2 text-center">{skill.name}</p> <p className="mt-2 text-center">{skill.name}</p>

View File

@ -2,9 +2,10 @@ export interface TooltipProps {
children: React.ReactNode children: React.ReactNode
label: string label: string
position: 'top' | 'bottom' | 'left' | 'right' position: 'top' | 'bottom' | 'left' | 'right'
additionalClass?: string
} }
export const Tooltip: React.FC<TooltipProps> = ({ children, label, position }) => { export const Tooltip: React.FC<TooltipProps> = ({ children, label, position, additionalClass }) => {
const tooltipStyles = { const tooltipStyles = {
top: 'bottom-full left-1/2 -translate-x-1/2', top: 'bottom-full left-1/2 -translate-x-1/2',
bottom: 'top-full left-1/2 -translate-x-1/2', bottom: 'top-full left-1/2 -translate-x-1/2',
@ -12,7 +13,7 @@ export const Tooltip: React.FC<TooltipProps> = ({ children, label, position }) =
right: 'left-full top-1/2 -translate-y-1/2', right: 'left-full top-1/2 -translate-y-1/2',
} }
const tooltipPosition: string = tooltipStyles[position] const tooltipPosition: string = tooltipStyles[position]
const tooltipClass = `pointer-events-none absolute mt-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 ${tooltipPosition}` const tooltipClass = `pointer-events-none absolute mt-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 ${tooltipPosition} ${additionalClass}`
return ( return (
<div className="group relative"> <div className="group relative">

View File

@ -1,4 +1,6 @@
import { Linkedin, Github, Award, Link } from 'lucide-react' import { Linkedin, Github, Award, Link } from 'lucide-react'
import { Aperture, Instagram, Mail, Globe, ScrollText } from 'lucide-react'
import { Camera, Plane, Film, Server, Cpu, Trophy, Car, Gamepad2 } from 'lucide-react'
import pythonIcon from './assets/python.svg' import pythonIcon from './assets/python.svg'
import robotIcon from './assets/robotframework-svgrepo-com.svg' import robotIcon from './assets/robotframework-svgrepo-com.svg'
import goIcon from './assets/go-original.svg' import goIcon from './assets/go-original.svg'
@ -86,23 +88,58 @@ export const PROJECTS = [
}, },
] ]
export const SOCIALLINKS = [ export const iconClass = 'text-blue-600 dark:text-blue-400 mr-3'
{
icon: <Linkedin size={32} />, export const SOCIALLINKS = {
href: 'https://www.linkedin.com/in/taqi-tahmid/', contact: [
label: 'LinkedIn', {
}, icon: <Mail size={20} className={iconClass} />,
{ href: 'mailto:taqitahmid@gmail.com',
icon: <Github size={32} />, text: 'Email Me',
href: 'https://github.com/TheTaqiTahmid', },
label: 'GitHub', {
}, icon: <Globe size={20} className={iconClass} />,
{ href: 'https://portfolio.tahmidcloud.com/',
icon: <Award size={32} />, text: 'Website',
href: 'https://ti-user-certificates.s3.amazonaws.com/e0df7fbf-a057-42af-8a1f-590912be5460/3da54db2-f994-4148-a0ca-705ae1d748cd-mohammad-taqi-tahmid-094cf8b4-0db8-4a9f-b787-b4efbb2a90fe-certificate.pdf', },
label: 'CKA Certificate', ],
}, connect: [
] {
icon: <Linkedin size={20} className={iconClass} />,
href: 'https://www.linkedin.com/in/taqi-tahmid/',
text: 'LinkedIn',
},
{
icon: <Github size={20} className={iconClass} />,
href: 'https://github.com/theTaqiTahmid',
text: 'GitHub',
},
],
follow: [
{
icon: <Aperture size={20} className={iconClass} />,
href: 'https://500px.com/p/taqi1203050?view=photos',
text: 'Photography',
},
{
icon: <Instagram size={20} className={iconClass} />,
href: 'https://www.instagram.com/tahmidtaqi/',
text: 'Instagram',
},
],
publications: [
{
icon: <Award size={20} className={iconClass} />,
href: 'https://www.credly.com/badges/abb049aa-d811-4954-a460-8c7351ceba3e/public_url',
text: 'CKA Certification',
},
{
icon: <ScrollText size={20} className={iconClass} />,
href: 'https://scholar.google.fi/citations?user=w3BoP0AAAAAJ&hl=en',
text: 'Google Scholar',
},
],
}
export const SKILLS = [ export const SKILLS = [
{ name: 'Python', icon: pythonIcon }, { name: 'Python', icon: pythonIcon },
@ -119,6 +156,68 @@ export const SKILLS = [
{ name: 'Grafana', icon: grafanaIcon }, { name: 'Grafana', icon: grafanaIcon },
] ]
export 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.',
},
],
},
]
export const EXPERIENCE = [ export const EXPERIENCE = [
{ {
title: 'Experienced Developer (DevOps)', title: 'Experienced Developer (DevOps)',

View File

@ -1,5 +1,5 @@
import { Building2, Calendar, GraduationCap, MapPin, Microscope, Wrench } from 'lucide-react' import { Building2, Calendar, GraduationCap, MapPin, Microscope, Wrench } from 'lucide-react'
import { COLORS, EXPERIENCE, EDUCATION, } from '../constants' import { COLORS, EXPERIENCE, EDUCATION } from '../constants'
const Experience = () => { const Experience = () => {
return ( return (

View File

@ -1,69 +1,6 @@
import { Camera, Plane, Film, Server, Cpu, Trophy, Car, Gamepad2 } from 'lucide-react' import { COLORS, INTERESTS } from '../constants'
import { COLORS } from '../constants'
const Interests = () => { 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 ( return (
<div className="p-4 max-w-4xl mx-auto"> <div className="p-4 max-w-4xl mx-auto">
<h1 <h1
@ -73,7 +10,7 @@ const Interests = () => {
</h1> </h1>
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 md:grid-cols-2">
{interests.map((interest, index) => ( {INTERESTS.map((interest, index) => (
<div <div
key={index} key={index}
className={`group p-6 rounded-lg border-2 border-gray-300 dark:border-gray-700 hover:shadow-lg transition-all duration-300 ${ className={`group p-6 rounded-lg border-2 border-gray-300 dark:border-gray-700 hover:shadow-lg transition-all duration-300 ${