Compare commits
	
		
			80 Commits
		
	
	
		
			1d45fca783
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| dfcea79f84 | |||
| 06fbf2e4de | |||
| b453dd3b9e | |||
| 46dc855a06 | |||
| 623a909e43 | |||
| 9a390bb831 | |||
| 0e3ae13474 | |||
| dba043a4af | |||
| 8a2536dd5d | |||
| 1d803dbe64 | |||
| 99eb2193cd | |||
| ea5e6f2f06 | |||
| fc3bf98fbd | |||
| 1c04f377bb | |||
| d0a576e76f | |||
| 345bc2c08a | |||
| 9d2c2b7bcd | |||
| 2a25da1e77 | |||
| 2209f36943 | |||
| 4c6bb29f9c | |||
| 173b2778ce | |||
| f28607e405 | |||
| dc3b4628ec | |||
| 12d7e880d9 | |||
| c427234a45 | |||
| b57b11c469 | |||
| e40252de99 | |||
| 8c39e38fb3 | |||
| a298941e0c | |||
| a516f020ef | |||
| 7f27e0c673 | |||
| 4879071e08 | |||
| 45410eaf4f | |||
| 280511118c | |||
| ae8a3643a4 | |||
| 5c9d8de03d | |||
| f980da7daf | |||
| e7c9e6d4e0 | |||
| 0cdb868e6e | |||
| 7d29c9abbc | |||
| c8ca07904b | |||
| 649f30075d | |||
| 0052dbe30f | |||
| 897b04fa4f | |||
| 4f2f98988f | |||
| 9dd04356fc | |||
| 9654ea3050 | |||
| 82c8b1d451 | |||
| b5877f36f5 | |||
| cbca63c43f | |||
| 9f95564022 | |||
| c451ec5b92 | |||
| 4c0ad887b5 | |||
| c7746ff91d | |||
| aad5dc83e1 | |||
| c9898eb26c | |||
| 2a5702db4b | |||
| 2badabb167 | |||
| 3f5facc265 | |||
| 9d15e4e50e | |||
| b45468fcbd | |||
| e5534d4f1d | |||
| a3580be305 | |||
| 47660ff80c | |||
| 940435cc8a | |||
| 6d76e17613 | |||
| 1d37e5a39f | |||
| 9cdb13c753 | |||
| 97d9372642 | |||
| 44d312ab4e | |||
| 8fcf4e8758 | |||
| 8ff3487198 | |||
| 9cd3573a00 | |||
| a8824e3864 | |||
| 92913fb20f | |||
| 672d3df1af | |||
| 6aa16bd0ef | |||
| a50caacfce | |||
| 71b095a0e4 | |||
| c7e09cb8c4 | 
							
								
								
									
										73
									
								
								.gitea_deprecated/workflows/build.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								.gitea_deprecated/workflows/build.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | |||||||
|  | name: Build the portfolio website | ||||||
|  | on: | ||||||
|  |   workflow_dispatch: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  |   pull_request: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   build-portfolio-website: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout code | ||||||
|  |         uses: actions/checkout@v2 | ||||||
|  |  | ||||||
|  |       - name: Set up Node.js | ||||||
|  |         uses: actions/setup-node@v2 | ||||||
|  |         with: | ||||||
|  |           node-version: "20" | ||||||
|  |  | ||||||
|  |       - name: Install dependencies | ||||||
|  |         run: cd frontend && npm install | ||||||
|  |  | ||||||
|  |       - name: Build the project | ||||||
|  |         run: cd frontend && npm run build | ||||||
|  |  | ||||||
|  |   build-and-release-image: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     container: catthehacker/ubuntu:act-latest | ||||||
|  |     needs: build-portfolio-website | ||||||
|  |     if: success() | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout | ||||||
|  |         uses: actions/checkout@v4 | ||||||
|  |         with: | ||||||
|  |           fetch-depth: 0 # all history for all branches and tags | ||||||
|  |  | ||||||
|  |       - name: Create kubeconfig | ||||||
|  |         run: | | ||||||
|  |           mkdir -p ~/.kube | ||||||
|  |           echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config | ||||||
|  |           chmod 600 ~/.kube/config | ||||||
|  |  | ||||||
|  |       - name: Set up Docker Buildx | ||||||
|  |         uses: docker/setup-buildx-action@v3 | ||||||
|  |         with: | ||||||
|  |           driver: kubernetes | ||||||
|  |           driver-opts: | | ||||||
|  |             namespace=gitea | ||||||
|  |             qemu.install=true | ||||||
|  |  | ||||||
|  |       - name: Login to Docker registry | ||||||
|  |         uses: docker/login-action@v3 | ||||||
|  |         with: | ||||||
|  |           registry: ${{ secrets.DOCKER_REGISTRY }} | ||||||
|  |           username: ${{ secrets.DOCKER_USERNAME }} | ||||||
|  |           password: ${{ secrets.DOCKER_PASSWORD }} | ||||||
|  |  | ||||||
|  |       - name: Generate timestamp | ||||||
|  |         id: timestamp | ||||||
|  |         run: echo "timestamp=$(date +%Y%m%d%H%M%S)" >> $GITEA_OUTPUT | ||||||
|  |  | ||||||
|  |       - name: Build Docker image | ||||||
|  |         uses: docker/build-push-action@v5 | ||||||
|  |         with: | ||||||
|  |           context: . | ||||||
|  |           file: Dockerfile | ||||||
|  |           push: true | ||||||
|  |           tags: | | ||||||
|  |             ${{ secrets.DOCKER_REGISTRY }}/taqi/portfolio/my-portfolio-app:latest | ||||||
|  |             ${{ secrets.DOCKER_REGISTRY }}/taqi/portfolio/my-portfolio-app:1.0.0-${{ steps.timestamp.outputs.timestamp }} | ||||||
							
								
								
									
										9
									
								
								.trivyignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.trivyignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | CVE-2024-4068 | ||||||
|  | CVE-2024-21538 | ||||||
|  | CVE-2024-21536 | ||||||
|  | CVE-2025-7783 | ||||||
|  | CVE-2024-29415 | ||||||
|  | CVE-2022-23539 | ||||||
|  | CVE-2022-24771 | ||||||
|  | CVE-2022-24772 | ||||||
|  | CVE-2024-29180 | ||||||
							
								
								
									
										35
									
								
								.woodpecker/build.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.woodpecker/build.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | when: | ||||||
|  |   - event: [push, pull_request, manual] | ||||||
|  |     branch: [master, feature/*] | ||||||
|  |  | ||||||
|  | steps: | ||||||
|  |   - name: trivy-scan | ||||||
|  |     image: aquasec/trivy:latest | ||||||
|  |     commands: | ||||||
|  |       - trivy fs --scanners vuln,config --exit-code 1  --ignorefile .trivyignore --severity HIGH,CRITICAL frontend/ | ||||||
|  |  | ||||||
|  |   - name: lint-frontend | ||||||
|  |     image: node:24 | ||||||
|  |     commands: | ||||||
|  |       - cd frontend | ||||||
|  |       - npm install | ||||||
|  |       - npm run lint | ||||||
|  |  | ||||||
|  |   - name: build-and-publish | ||||||
|  |     image: woodpeckerci/plugin-docker-buildx | ||||||
|  |     settings: | ||||||
|  |       registry: | ||||||
|  |         from_secret: docker_registry | ||||||
|  |       repo: | ||||||
|  |         from_secret: portfolio_docker_repo | ||||||
|  |       tags: | ||||||
|  |         - latest | ||||||
|  |         - 1.0.0-${CI_PIPELINE_NUMBER} # Ref: https://woodpecker-ci.org/docs/usage/environment | ||||||
|  |       skip_tls_verify: false # set to true for testing registries ONLY with self-signed certs | ||||||
|  |       build_args: | ||||||
|  |         - COMMIT_SHA=${CI_COMMIT_SHA} | ||||||
|  |         - COMMIT_AUTHOR_EMAIL=${CI_COMMIT_AUTHOR_EMAIL} | ||||||
|  |       username: | ||||||
|  |         from_secret: docker-username | ||||||
|  |       password: | ||||||
|  |         from_secret: docker-password | ||||||
| @ -1,5 +1,5 @@ | |||||||
| # Step 1: Build the React app | # Step 1: Build the React app | ||||||
| FROM node:20 AS build | FROM node:24-slim AS build | ||||||
|  |  | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							| @ -1,5 +1,4 @@ | |||||||
| My Portfolio Website | # My Portfolio Website | ||||||
| ===================== |  | ||||||
|  |  | ||||||
| # Overview | # Overview | ||||||
|  |  | ||||||
| @ -31,4 +30,19 @@ docker push $DOCKER_REGISTRY/my-portfolio-app:latest | |||||||
|  |  | ||||||
| # Check the registry | # Check the registry | ||||||
| curl -u user:pass https://$DOCKER_REGISTRY/v2/_catalog | curl -u user:pass https://$DOCKER_REGISTRY/v2/_catalog | ||||||
|  |  | ||||||
|  | # Or if using gitea registry | ||||||
|  | curl --netrc -X GET https://gitea.yourdomain.com/v2/_catalog | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | # CI/CD | ||||||
|  |  | ||||||
|  | Run in Gitea Actions within kubernetes cluster | ||||||
|  |  | ||||||
|  | Current, the project has workflow files for: | ||||||
|  |  | ||||||
|  | - Build and push the container to the registry | ||||||
|  | - Deploy the container to the Kubernetes cluster | ||||||
|  |  | ||||||
|  | Thus making a commit to the master branch will automatically build the | ||||||
|  | container and deploy it to the cluster. | ||||||
|  | |||||||
							
								
								
									
										17197
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										17197
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -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", | ||||||
| @ -35,6 +36,9 @@ | |||||||
|     "tailwindcss": "^3.4.11", |     "tailwindcss": "^3.4.11", | ||||||
|     "typescript": "^5.5.3", |     "typescript": "^5.5.3", | ||||||
|     "typescript-eslint": "^8.0.1", |     "typescript-eslint": "^8.0.1", | ||||||
|     "vite": "^5.4.1" |     "vite": "^7.0.6" | ||||||
|  |   }, | ||||||
|  |   "overrides": { | ||||||
|  |     "@types/minimatch": "5.1.2" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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,6 +25,11 @@ 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} /> | ||||||
|  |             <div className="md:flex md:flex-row md:h-full"> | ||||||
|  |               <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"> | ||||||
|  |                 <Sidebar /> | ||||||
|  |               </div> | ||||||
|  |               <div className="md:flex-1"> | ||||||
|                 <Router> |                 <Router> | ||||||
|                   <Routes> |                   <Routes> | ||||||
|                     <Route path="/" element={<Home />} /> |                     <Route path="/" element={<Home />} /> | ||||||
| @ -32,6 +38,9 @@ function App() { | |||||||
|                     <Route path="/Interests" element={<Interests />} /> |                     <Route path="/Interests" element={<Interests />} /> | ||||||
|                   </Routes> |                   </Routes> | ||||||
|                 </Router> |                 </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> | ||||||
|  | |||||||
| @ -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> | ||||||
|  | |||||||
| @ -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> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,11 +35,13 @@ const Navbar: React.FC<NavProps> = ({ toggleDarkMode, darkMode }) => { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (isMenuOpen) { | ||||||
|       document.addEventListener('mousedown', handleClickoutside) |       document.addEventListener('mousedown', handleClickoutside) | ||||||
|  |     } | ||||||
|     return () => { |     return () => { | ||||||
|       document.removeEventListener('mousedown', handleClickoutside) |       document.removeEventListener('mousedown', handleClickoutside) | ||||||
|     } |     } | ||||||
|   }) |   }, [isMenuOpen]) | ||||||
|  |  | ||||||
|   const menuItem = [ |   const menuItem = [ | ||||||
|     { title: 'Home', href: '/' }, |     { title: 'Home', href: '/' }, | ||||||
| @ -102,7 +104,8 @@ const Navbar: React.FC<NavProps> = ({ toggleDarkMode, darkMode }) => { | |||||||
|  |  | ||||||
|               {/* Dropdown Menu */} |               {/* Dropdown Menu */} | ||||||
|               {isMenuOpen && ( |               {isMenuOpen && ( | ||||||
|                 <div className="absolute right-0 mt-3 w-36 rounded-md shadow-lg bg-amber-50 dark:bg-gray-800 ring-1 ring-black ring-opacity-5"> |                 // Dropdown Menu. The z value is set to 10 so that it appears above other elements. | ||||||
|  |                 <div className="absolute right-0 mt-3 w-36 rounded-md shadow-lg bg-amber-50 dark:bg-gray-800 ring-1 ring-black ring-opacity-5 z-10"> | ||||||
|                   <div className="py-1" role="menu" aria-orientation="vertical"> |                   <div className="py-1" role="menu" aria-orientation="vertical"> | ||||||
|                     {menuItem.map((item) => ( |                     {menuItem.map((item) => ( | ||||||
|                       <a |                       <a | ||||||
|  | |||||||
							
								
								
									
										75
									
								
								frontend/src/components/Sidebar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								frontend/src/components/Sidebar.tsx
									
									
									
									
									
										Normal 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 | ||||||
| @ -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> | ||||||
|  | |||||||
| @ -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"> | ||||||
|  | |||||||
| @ -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' | ||||||
| @ -26,7 +28,6 @@ export const EMAIL = 'taqitahmid@gmail.com' | |||||||
| export const RESUME = | export const RESUME = | ||||||
|   'https://www.linkedin.com/in/taqi-tahmid/overlay/1735981754176/single-media-viewer/?profileId=ACoAACDU_GsBCgKtvw2bmzbVwTy2WixBG6-e3JM' |   'https://www.linkedin.com/in/taqi-tahmid/overlay/1735981754176/single-media-viewer/?profileId=ACoAACDU_GsBCgKtvw2bmzbVwTy2WixBG6-e3JM' | ||||||
|  |  | ||||||
|  |  | ||||||
| export const PROJECTS = [ | export const PROJECTS = [ | ||||||
|   { |   { | ||||||
|     title: 'Self-Hosted Kubernetes Homelab Cluster', |     title: 'Self-Hosted Kubernetes Homelab Cluster', | ||||||
| @ -87,23 +88,58 @@ export const PROJECTS = [ | |||||||
|   }, |   }, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| export const SOCIALLINKS = [ | export const iconClass = 'text-blue-600 dark:text-blue-400 mr-3' | ||||||
|  |  | ||||||
|  | export const SOCIALLINKS = { | ||||||
|  |   contact: [ | ||||||
|     { |     { | ||||||
|     icon: <Linkedin size={32} />, |       icon: <Mail size={20} className={iconClass} />, | ||||||
|  |       href: 'mailto:taqitahmid@gmail.com', | ||||||
|  |       text: 'Email Me', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       icon: <Globe size={20} className={iconClass} />, | ||||||
|  |       href: 'https://portfolio.tahmidcloud.com/', | ||||||
|  |       text: 'Website', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |   connect: [ | ||||||
|  |     { | ||||||
|  |       icon: <Linkedin size={20} className={iconClass} />, | ||||||
|       href: 'https://www.linkedin.com/in/taqi-tahmid/', |       href: 'https://www.linkedin.com/in/taqi-tahmid/', | ||||||
|     label: 'LinkedIn', |       text: 'LinkedIn', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|     icon: <Github size={32} />, |       icon: <Github size={20} className={iconClass} />, | ||||||
|     href: 'https://github.com/TheTaqiTahmid', |       href: 'https://github.com/theTaqiTahmid', | ||||||
|     label: 'GitHub', |       text: 'GitHub', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |   follow: [ | ||||||
|  |     { | ||||||
|  |       icon: <Aperture size={20} className={iconClass} />, | ||||||
|  |       href: 'https://500px.com/p/taqi1203050?view=photos', | ||||||
|  |       text: 'Photography', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|     icon: <Award size={32} />, |       icon: <Instagram size={20} className={iconClass} />, | ||||||
|     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', |       href: 'https://www.instagram.com/tahmidtaqi/', | ||||||
|     label: 'CKA Certificate', |       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,3 +155,128 @@ export const SKILLS = [ | |||||||
|   { name: 'Prometheus', icon: prometheusIcon }, |   { name: 'Prometheus', icon: prometheusIcon }, | ||||||
|   { 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 = [ | ||||||
|  |   { | ||||||
|  |     title: 'Experienced Developer (DevOps)', | ||||||
|  |     company: 'Ericsson', | ||||||
|  |     location: 'Jorvas, Finland', | ||||||
|  |     period: 'November-2022 - Present', | ||||||
|  |     responsibilities: [ | ||||||
|  |       'Managing and optimizing Kubernetes clusters in production environments', | ||||||
|  |       'Designing and implementing CI/CD pipelines for end to end product development flow using Jenkins', | ||||||
|  |       'Automating infrastructure deployment using Terraform and Ansible', | ||||||
|  |       'Develop and maintain monitoring solutions of various resources for greater observability and troubleshooting', | ||||||
|  |       'Actively support development teams regarding product development flow and infrastructure issues', | ||||||
|  |       'Develop and perform automated end-to-end product testing with Python, Robot Framework, Jenkins, Bash, etc.', | ||||||
|  |     ], | ||||||
|  |     tools: 'Kubernetes, Docker, KVM, Openstack, Ansible, Terraform, Prometheus, Grafana', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     title: 'Test Engineer', | ||||||
|  |     company: 'Nokia', | ||||||
|  |     location: 'Espoo, Finland', | ||||||
|  |     period: 'June-2021 - October-2022', | ||||||
|  |     responsibilities: [ | ||||||
|  |       'Develop and maintain Cloud RAN E2E test setup for vCU and vDU application testing on top of RedHat Openshift', | ||||||
|  |       'Develop automation and CI/CD flow for Cloud RAN testing using Python, Robot Framework, Bash, Jenkins etc.', | ||||||
|  |       'Develop and perform automated testing to validate the functionality of Nokia Cloud RAN base stations', | ||||||
|  |       'Integrate new hardware and software into the test setup', | ||||||
|  |       'Perform hands on debugging and log analysis to nd root cause and solve any software or hardware issues', | ||||||
|  |     ], | ||||||
|  |     tools: 'Keysight Nemo Outdoor, Nemo Analyze, Qualcomm PCAT, QCAT, QXDM', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     title: 'Testing and Prototyping Intern', | ||||||
|  |     company: 'GE Healthcare', | ||||||
|  |     location: 'Helsinki, Finland', | ||||||
|  |     period: 'Jan-2019 - May-2021', | ||||||
|  |     responsibilities: [ | ||||||
|  |       'Planning, writing, and performing manual and automated tests of different prototype wireless medical devices', | ||||||
|  |       'Designing driver and PCB circuits in Altium Designer to test the performance of the Digital Sensor Interface', | ||||||
|  |       'Ensuring the PCB componets used in the devices are EU RoHS and REACH compliant', | ||||||
|  |     ], | ||||||
|  |     tools: | ||||||
|  |       'LTSpice, Altium Designer, HP-ALM, Vector Network Analyzer, Spectrum Analyzer, Climate Chamber', | ||||||
|  |   }, | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | export const EDUCATION = [ | ||||||
|  |   { | ||||||
|  |     degree: "Master's in Wireless Communication & RF Systems", | ||||||
|  |     institution: 'Tampere University', | ||||||
|  |     location: 'Tampere, Finland', | ||||||
|  |     period: '2018 - 2020', | ||||||
|  |     thesis: '5G Reference Signals and their Possibility to be for 5G Based Positioning', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     degree: "Bachelor's in Electrical & Electronic Engineering", | ||||||
|  |     institution: 'Khulna University of Engineering & Technology', | ||||||
|  |     location: 'Khulna, Bangladesh', | ||||||
|  |     period: '2013 - 2017', | ||||||
|  |     thesis: | ||||||
|  |       'Density-based smart traffic control system using Canny edge detection technique using Digital Image Processing', | ||||||
|  |   }, | ||||||
|  | ] | ||||||
|  | |||||||
| @ -1,70 +1,7 @@ | |||||||
| import { Building2, Calendar, GraduationCap, MapPin, Microscope, Wrench } from 'lucide-react' | import { Building2, Calendar, GraduationCap, MapPin, Microscope, Wrench } from 'lucide-react' | ||||||
| import { COLORS } from '../constants' | import { COLORS, EXPERIENCE, EDUCATION } from '../constants' | ||||||
|  |  | ||||||
| const Experience = () => { | const Experience = () => { | ||||||
|   const experiences = [ |  | ||||||
|     { |  | ||||||
|       title: 'Experienced Developer (DevOps)', |  | ||||||
|       company: 'Ericsson', |  | ||||||
|       location: 'Jorvas, Finland', |  | ||||||
|       period: 'November-2022 - Present', |  | ||||||
|       responsibilities: [ |  | ||||||
|         'Managing and optimizing Kubernetes clusters in production environments', |  | ||||||
|         'Designing and implementing CI/CD pipelines for end to end product development flow using Jenkins', |  | ||||||
|         'Automating infrastructure deployment using Terraform and Ansible', |  | ||||||
|         'Develop and maintain monitoring solutions of various resources for greater observability and troubleshooting', |  | ||||||
|         'Actively support development teams regarding product development flow and infrastructure issues', |  | ||||||
|         'Develop and perform automated end-to-end product testing with Python, Robot Framework, Jenkins, Bash, etc.', |  | ||||||
|       ], |  | ||||||
|       tools: 'Kubernetes, Docker, KVM, Openstack, Ansible, Terraform, Prometheus, Grafana', |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       title: 'Test Engineer', |  | ||||||
|       company: 'Nokia', |  | ||||||
|       location: 'Espoo, Finland', |  | ||||||
|       period: 'June-2021 - October-2022', |  | ||||||
|       responsibilities: [ |  | ||||||
|         'Develop and maintain Cloud RAN E2E test setup for vCU and vDU application testing on top of RedHat Openshift', |  | ||||||
|         'Develop automation and CI/CD flow for Cloud RAN testing using Python, Robot Framework, Bash, Jenkins etc.', |  | ||||||
|         'Develop and perform automated testing to validate the functionality of Nokia Cloud RAN base stations', |  | ||||||
|         'Integrate new hardware and software into the test setup', |  | ||||||
|         'Perform hands on debugging and log analysis to nd root cause and solve any software or hardware issues', |  | ||||||
|       ], |  | ||||||
|       tools: 'Keysight Nemo Outdoor, Nemo Analyze, Qualcomm PCAT, QCAT, QXDM', |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       title: 'Testing and Prototyping Intern', |  | ||||||
|       company: 'GE Healthcare', |  | ||||||
|       location: 'Helsinki, Finland', |  | ||||||
|       period: 'Jan-2019 - May-2021', |  | ||||||
|       responsibilities: [ |  | ||||||
|         'Planning, writing, and performing manual and automated tests of different prototype wireless medical devices', |  | ||||||
|         'Designing driver and PCB circuits in Altium Designer to test the performance of the Digital Sensor Interface', |  | ||||||
|         'Ensuring the PCB componets used in the devices are EU RoHS and REACH compliant', |  | ||||||
|       ], |  | ||||||
|       tools: |  | ||||||
|         'LTSpice, Altium Designer, HP-ALM, Vector Network Analyzer, Spectrum Analyzer, Climate Chamber', |  | ||||||
|     }, |  | ||||||
|   ] |  | ||||||
|  |  | ||||||
|   const education = [ |  | ||||||
|     { |  | ||||||
|       degree: "Master's in Wireless Communication & RF Systems", |  | ||||||
|       institution: 'Tampere University', |  | ||||||
|       location: 'Tampere, Finland', |  | ||||||
|       period: '2018 - 2020', |  | ||||||
|       thesis: '5G Reference Signals and their Possibility to be for 5G Based Positioning', |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       degree: "Bachelor's in Electrical & Electronic Engineering", |  | ||||||
|       institution: 'Khulna University of Engineering & Technology', |  | ||||||
|       location: 'Khulna, Bangladesh', |  | ||||||
|       period: '2013 - 2017', |  | ||||||
|       thesis: |  | ||||||
|         'Density-based smart traffic control system using Canny edge detection technique using Digital Image Processing', |  | ||||||
|     }, |  | ||||||
|   ] |  | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="p-4 max-w-4xl mx-auto"> |     <div className="p-4 max-w-4xl mx-auto"> | ||||||
|       <h2 |       <h2 | ||||||
| @ -74,7 +11,7 @@ const Experience = () => { | |||||||
|       </h2> |       </h2> | ||||||
|  |  | ||||||
|       <div className="space-y-8"> |       <div className="space-y-8"> | ||||||
|         {experiences.map((exp, index) => ( |         {EXPERIENCE.map((exp, index) => ( | ||||||
|           <div |           <div | ||||||
|             key={index} |             key={index} | ||||||
|             className={`border-l-4 ${COLORS.BORDER} ${COLORS.DARK_BORDER} pl-4 space-y-2`} |             className={`border-l-4 ${COLORS.BORDER} ${COLORS.DARK_BORDER} pl-4 space-y-2`} | ||||||
| @ -128,7 +65,7 @@ const Experience = () => { | |||||||
|       </h2> |       </h2> | ||||||
|  |  | ||||||
|       <div className="space-y-6"> |       <div className="space-y-6"> | ||||||
|         {education.map((edu, index) => ( |         {EDUCATION.map((edu, index) => ( | ||||||
|           <div |           <div | ||||||
|             key={index} |             key={index} | ||||||
|             className={`border-l-4 ${COLORS.BORDER} ${COLORS.DARK_BORDER} pl-4 space-y-2`} |             className={`border-l-4 ${COLORS.BORDER} ${COLORS.DARK_BORDER} pl-4 space-y-2`} | ||||||
|  | |||||||
| @ -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 ${ | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| {"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/Introduction.tsx","./src/components/Navbar.tsx","./src/components/Photo.tsx","./src/components/Skills.tsx"],"version":"5.6.2"} | {"root":["./src/App.tsx","./src/constants.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/Footer.tsx","./src/components/Introduction.tsx","./src/components/Navbar.tsx","./src/components/Photo.tsx","./src/components/Sidebar.tsx","./src/components/Skills.tsx","./src/components/Tooltip.tsx","./src/pages/Experience.tsx","./src/pages/Home.tsx","./src/pages/Interests.tsx","./src/pages/Projects.tsx"],"version":"5.8.3"} | ||||||
| @ -1 +1 @@ | |||||||
| {"root":["./vite.config.ts"],"version":"5.6.2"} | {"root":["./vite.config.ts"],"version":"5.8.3"} | ||||||
							
								
								
									
										144
									
								
								scripts/create_kubeconfig.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										144
									
								
								scripts/create_kubeconfig.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,144 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | function usage() { | ||||||
|  |     echo "--namespace <namespace> --user <user>  --kubeconfig <kubeconfig>" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function create_namespace() { | ||||||
|  |     local namespace="$1" | ||||||
|  |     kubectl create namespace "$namespace" \ | ||||||
|  |         --dry-run=client -o yaml | kubectl apply -f - | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function create_service_account() { | ||||||
|  |     local user="$1" | ||||||
|  |     local namespace="$2" | ||||||
|  |  | ||||||
|  |     kubectl create serviceaccount "$user" \ | ||||||
|  |         --namespace "$namespace" \ | ||||||
|  |         --dry-run=client -o yaml | kubectl apply -f - | ||||||
|  |  | ||||||
|  |     # Create associated secret | ||||||
|  |     kubectl apply -f - <<EOF | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: Secret | ||||||
|  | metadata: | ||||||
|  |   name: ${user}-secret | ||||||
|  |   namespace: ${namespace} | ||||||
|  |   annotations: | ||||||
|  |     kubernetes.io/service-account.name: ${user} | ||||||
|  | type: kubernetes.io/service-account-token | ||||||
|  | EOF | ||||||
|  |  | ||||||
|  |     echo "Service account $user created in namespace $namespace" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function create_role() { | ||||||
|  |     local user="$1" | ||||||
|  |     local namespace="$2" | ||||||
|  |  | ||||||
|  |     kubectl create role "$user" \ | ||||||
|  |         --verb=get,list,watch,create,update,delete,patch,exec \ | ||||||
|  |         --resource=pods,pods/exec,services,deployments,secrets,configmaps \ | ||||||
|  |         --namespace "$namespace" \ | ||||||
|  |         --dry-run=client -o yaml | kubectl apply -f - | ||||||
|  |  | ||||||
|  |     echo "Role $user created in namespace $namespace" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function create_role_binding() { | ||||||
|  |     local user="$1" | ||||||
|  |     local namespace="$2" | ||||||
|  |  | ||||||
|  |     kubectl create rolebinding "$user" \ | ||||||
|  |         --role="$user" \ | ||||||
|  |         --serviceaccount="$namespace:$user" \ | ||||||
|  |         --namespace "$namespace" \ | ||||||
|  |         --dry-run=client -o yaml | kubectl apply -f - | ||||||
|  |  | ||||||
|  |     echo "Role binding $user created in namespace $namespace" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function create_kubeconfig() { | ||||||
|  |     local user="$1" | ||||||
|  |     local namespace="$2" | ||||||
|  |     local kubeconfig="$3" | ||||||
|  |  | ||||||
|  |     SECRET_NAME=${user}-secret | ||||||
|  |     CLUSTER_NAME=$(kubectl config view --minify -o jsonpath='{.clusters[0].name}') | ||||||
|  |     TOKEN=$(kubectl get secret "$SECRET_NAME" -n "$namespace" -o jsonpath='{.data.token}' | base64 --decode) | ||||||
|  |     CA=$(kubectl get secret "$SECRET_NAME" -n "$namespace" -o jsonpath='{.data.ca\.crt}') | ||||||
|  |  | ||||||
|  |     SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}') | ||||||
|  |  | ||||||
|  |     # Create kubeconfig file with proper indentation | ||||||
|  |     cat >"${kubeconfig}" <<EOF | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: Config | ||||||
|  | clusters: | ||||||
|  | - name: ${CLUSTER_NAME} | ||||||
|  |   cluster: | ||||||
|  |     server: ${SERVER} | ||||||
|  |     certificate-authority-data: ${CA} | ||||||
|  | contexts: | ||||||
|  | - name: ${CLUSTER_NAME}-${namespace}-ci | ||||||
|  |   context: | ||||||
|  |     cluster: ${CLUSTER_NAME} | ||||||
|  |     namespace: ${namespace} | ||||||
|  |     user: ${user} | ||||||
|  | current-context: ${CLUSTER_NAME}-${namespace}-ci | ||||||
|  | users: | ||||||
|  | - name: ${user} | ||||||
|  |   user: | ||||||
|  |     token: ${TOKEN} | ||||||
|  | EOF | ||||||
|  |  | ||||||
|  |     echo "Kubeconfig file created at ${kubeconfig}" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | # Main script | ||||||
|  | function main() { | ||||||
|  |     local namespace="" | ||||||
|  |     local user="" | ||||||
|  |     local kubeconfig="" | ||||||
|  |  | ||||||
|  |     while [[ "$#" -gt 0 ]]; do | ||||||
|  |         case $1 in | ||||||
|  |         -n|--namespace) | ||||||
|  |             namespace="$2" | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         -u|--user) | ||||||
|  |             user="$2" | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         -k|--kubeconfig) | ||||||
|  |             kubeconfig="$2" | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         -h|--help) | ||||||
|  |             usage | ||||||
|  |             exit 0 | ||||||
|  |             ;; | ||||||
|  |         *) | ||||||
|  |             usage | ||||||
|  |             exit 1 | ||||||
|  |             ;; | ||||||
|  |         esac | ||||||
|  |         shift | ||||||
|  |     done | ||||||
|  |  | ||||||
|  |     if [[ -z "$namespace" || -z "$user" || -z "$kubeconfig" ]]; then | ||||||
|  |         usage | ||||||
|  |         exit 1 | ||||||
|  |     fi | ||||||
|  |  | ||||||
|  |     create_namespace "$namespace" | ||||||
|  |     create_service_account "$user" "$namespace" | ||||||
|  |     create_role "$user" "$namespace" | ||||||
|  |     create_role_binding "$user" "$namespace" | ||||||
|  |     create_kubeconfig "$user" "$namespace" "$kubeconfig" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | # Call the main function | ||||||
|  | main "$@" | ||||||
		Reference in New Issue
	
	Block a user