diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..97e47a1 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,85 @@ +name: Deploy Next.js site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 8 + run_install: false + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Setup Pages + uses: actions/configure-pages@v4 + with: + # Automatically inject basePath in your Next.js configuration file and disable + # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). + # + # You may remove this line if you want to manage the configuration yourself. + static_exports: true + + - name: Build with Next.js + run: pnpm build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./out + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index fd3dbb5..28caed9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,13 @@ # dependencies /node_modules /.pnp -.pnp.js -.yarn/install-state.gz +/.pnpm-store +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions # testing /coverage @@ -24,9 +29,10 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.pnpm-debug.log* -# local env files -.env*.local +# env files (can opt-in for committing if needed) +.env* # vercel .vercel diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index e43643f..0000000 --- a/.gitpod.yml +++ /dev/null @@ -1,11 +0,0 @@ -# This configuration file was automatically generated by Gitpod. -# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) -# and commit this file to your remote git repository to share the goodness with others. - -# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart - -tasks: - - init: npm install && npm run build - command: npm run start - - diff --git a/CNAME b/CNAME deleted file mode 100644 index fcf5778..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -sayfullaheid.me \ No newline at end of file diff --git a/CONTENT_LICENSE b/CONTENT_LICENSE new file mode 100644 index 0000000..0cb7d7e --- /dev/null +++ b/CONTENT_LICENSE @@ -0,0 +1,20 @@ +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) + +This license applies to all original written blog content, articles, and media files +(e.g., images, diagrams) in this repository, excluding source code. + +You are free to: + +- Share — copy and redistribute the material in any medium or format +- Adapt — remix, transform, and build upon the material + +Under the following terms: + +- Attribution — You must give appropriate credit, provide a link to the license, + and indicate if changes were made. You may do so in any reasonable manner, + but not in any way that suggests the licensor endorses you or your use. +- NonCommercial — You may not use the material for commercial purposes. +- ShareAlike — If you remix, transform, or build upon the material, + you must distribute your contributions under the same license as the original. + +Full license text: diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dadbc3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Sayfullah Eid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 0dc9ea2..016a13c 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,199 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +# Sayfullah Eid - Developer Portfolio -## Getting Started +A modern, responsive portfolio website built with Next.js 15, TypeScript, and Tailwind CSS. Features a clean design, blog functionality with MDX, and automatic deployment to GitHub Pages. -First, run the development server: +## ✨ Features -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +- **🏠 Welcome Page** - Modern hero section with skills showcase and quick links +- **💼 Experience Page** - Professional experience timeline with detailed achievements +- **🚀 Projects Page** - Interactive project showcase with featured and regular projects +- **📝 Blog** - MDX-powered blog with syntax highlighting and responsive design +- **🌙 Dark/Light Theme** - Automatic theme switching with system preference support +- **📱 Responsive Design** - Optimized for all device sizes +- **⚡ Fast Performance** - Static site generation for optimal loading times +- **🚀 Auto Deployment** - GitHub Actions workflow for automatic deployment to GitHub Pages + +## 🛠️ Tech Stack + +- **Framework**: Next.js 15 with App Router +- **Language**: TypeScript +- **Styling**: Tailwind CSS +- **UI Components**: Shadcn/ui +- **Blog**: MDX with gray-matter for frontmatter +- **Icons**: Lucide React +- **Deployment**: GitHub Pages +- **Package Manager**: pnpm + +## 📁 Project Structure + +``` +├── app/ # Next.js app directory +│ ├── blog/ # Blog pages +│ ├── experience/ # Experience page +│ ├── projects/ # Projects page +│ ├── layout.tsx # Root layout +│ └── page.tsx # Home page +├── components/ # Reusable components +│ ├── ui/ # Shadcn/ui components +│ ├── nav-menu.tsx # Navigation component +│ └── mdx-components.tsx # MDX component mappings +├── content/ # Content directory +│ └── blog/ # Blog post MDX files +├── lib/ # Utility functions +│ ├── blog.ts # Blog utilities +│ └── utils.ts # General utilities +├── .github/workflows/ # GitHub Actions +└── public/ # Static assets +``` + +## 🚀 Getting Started + +### Prerequisites + +- Node.js 18+ +- pnpm (recommended) or npm + +### Installation + +1. **Clone the repository** + + ```bash + git clone https://github.com/FusionStreak/FusionStreak.github.io.git + cd FusionStreak.github.io + ``` + +2. **Install dependencies** + + ```bash + pnpm install + ``` + +3. **Run the development server** + + ```bash + pnpm dev + ``` + +4. **Open in browser** + Navigate to [http://localhost:3000](http://localhost:3000) + +## 📝 Adding Content + +### Adding Blog Posts + +Create new MDX files in the `content/blog/` directory: + +```markdown +--- +title: "Your Post Title" +date: "2024-01-15" +excerpt: "A brief description of your post" +author: "Your Name" +tags: ["tag1", "tag2", "tag3"] +featured: true +readTime: "5 min read" +--- + +# Your Post Title + +Your content here using Markdown/MDX syntax... +``` + +### Adding Experience + +Edit the `experiences` array in `app/experience/page.tsx`: + +```typescript +const experiences: Experience[] = [ + { + id: "new-id", + title: "Your Job Title", + company: "Company Name", + location: "Location", + startDate: "2024-01", + endDate: undefined, // or "2024-12" for past roles + description: "Job description...", + achievements: ["Achievement 1", "Achievement 2"], + technologies: ["React", "TypeScript", "Node.js"], + website: "https://company.com" + }, + // ... existing experiences +]; ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +### Adding Projects + +Edit the `projects` array in `app/projects/page.tsx`: + +```typescript +const projects: Project[] = [ + { + id: "new-project", + title: "Project Name", + description: "Short description", + longDescription: "Detailed description...", + technologies: ["Next.js", "TypeScript"], + githubUrl: "https://github.com/username/repo", + liveUrl: "https://project-demo.com", + featured: true, + createdAt: "2024-01-15", + status: "completed" + }, + // ... existing projects +]; +``` + +## 🎨 Customization + +### Updating Personal Information -You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. +1. **Update metadata** in `app/layout.tsx` +2. **Modify hero section** in `app/page.tsx` +3. **Update social links** in `app/page.tsx` +4. **Change skills** in `app/page.tsx` + +### Styling + +- **Colors**: Modify CSS variables in `app/globals.css` +- **Components**: Customize Shadcn/ui components in `components/ui/` +- **Layout**: Adjust the main layout in `app/layout.tsx` + +## 🚀 Deployment + +### Automatic Deployment (Recommended) + +The repository includes a GitHub Actions workflow that automatically deploys to GitHub Pages when you push to the main branch. + +1. **Enable GitHub Pages** in your repository settings +2. **Set source** to "GitHub Actions" +3. **Push to main branch** - deployment will trigger automatically + +### Manual Deployment + +```bash +# Build the static site +pnpm build + +# The output will be in the 'out' directory +# Upload the contents to your hosting provider +``` -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. +## 📦 Scripts -## Learn More +- `pnpm dev` - Start development server +- `pnpm build` - Build for production +- `pnpm start` - Start production server +- `pnpm lint` - Run ESLint -To learn more about Next.js, take a look at the following resources: +## 🛡️ Licensing -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +This repository uses a dual-license model: -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +- **Code** (in `/app`, `/components`, etc.): Licensed under the [MIT License](./LICENSE) +- **Content** (in `/content`, `/public`, etc.): Licensed under [CC BY-NC 4.0](./CONTENT_LICENSE) -## Deploy on Vercel +If you want to reuse or republish parts of the content commercially, please contact me for permission. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +--- -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +Built with ❤️ by [Sayfullah Eid](https://github.com/FusionStreak) diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx new file mode 100644 index 0000000..7f1cccd --- /dev/null +++ b/app/blog/[slug]/page.tsx @@ -0,0 +1,139 @@ +import { notFound } from "next/navigation"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; +import { Calendar, Clock, User, ArrowLeft, Share2 } from "lucide-react"; +import Link from "next/link"; +import { getPostBySlug, getAllSlugs } from "@/lib/blog"; +import { MDXContent } from "@/components/mdx-content"; + +interface BlogPostPageProps { + params: Promise<{ + slug: string; + }>; +} + +export async function generateStaticParams() { + const slugs = getAllSlugs(); + return slugs.map((slug) => ({ + slug, + })); +} + +export async function generateMetadata({ params }: BlogPostPageProps) { + const { slug } = await params; + const post = await getPostBySlug(slug); + + if (!post) { + return { + title: 'Post Not Found', + }; + } + + return { + title: `${post.title} | Sayfullah Eid`, + description: post.excerpt, + }; +} + +export default async function BlogPostPage({ params }: BlogPostPageProps) { + const { slug } = await params; + const post = await getPostBySlug(slug); + + if (!post) { + notFound(); + } + + return ( +
+ {/* Header */} +
+ + +
+

+ {post.title} +

+ +
+
+ + {new Date(post.date).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} +
+
+ + {post.readTime} +
+ {post.author && ( +
+ + {post.author} +
+ )} + +
+ + {post.tags && post.tags.length > 0 && ( +
+ {post.tags.map((tag, idx) => ( + + {tag} + + ))} +
+ )} + + {post.excerpt && ( +

+ {post.excerpt} +

+ )} +
+
+ + + + {/* Content */} + + + {await MDXContent({ source: post.content })} + + + + {/* Footer */} +
+ + +
+
+

+ Thanks for reading! If you enjoyed this post, consider sharing it with others. +

+
+
+ + +
+
+
+
+ ); +} diff --git a/app/blog/page.tsx b/app/blog/page.tsx new file mode 100644 index 0000000..8b3e10f --- /dev/null +++ b/app/blog/page.tsx @@ -0,0 +1,155 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; +import { Calendar, Clock, User, ArrowRight } from "lucide-react"; +import Link from "next/link"; +import { getAllPosts, getFeaturedPosts } from "@/lib/blog"; + +export default function BlogPage() { + const allPosts = getAllPosts(); + const featuredPosts = getFeaturedPosts(); + const recentPosts = allPosts.filter(post => !post.featured).slice(0, 6); + + return ( +
+
+

+ Blog +

+

+ Thoughts, tutorials, and insights about web development, technology, and programming. +

+
+ + {featuredPosts.length > 0 && ( +
+

Featured Posts

+
+ {featuredPosts.slice(0, 2).map((post) => ( + + +
+
+ + {new Date(post.date).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} +
+
+ + {post.readTime} +
+ {post.author && ( +
+ + {post.author} +
+ )} +
+ + + {post.title} + + + + {post.excerpt} + +
+ + {post.tags && post.tags.length > 0 && ( +
+ {post.tags.slice(0, 3).map((tag, idx) => ( + + {tag} + + ))} + {post.tags.length > 3 && ( + + +{post.tags.length - 3} more + + )} +
+ )} + +
+
+ ))} +
+
+ )} + + {recentPosts.length > 0 && ( +
+

Recent Posts

+
+ {recentPosts.map((post, index) => ( +
+
+
+
+ {new Date(post.date).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + })} +
+
+ {post.readTime} +
+ {post.tags && post.tags.length > 0 && ( +
+ {post.tags.slice(0, 2).map((tag, idx) => ( + + {tag} + + ))} +
+ )} +
+
+

+ + {post.title} + +

+

+ {post.excerpt} +

+ +
+
+ {index < recentPosts.length - 1 && } +
+ ))} +
+
+ )} + + {allPosts.length === 0 && ( + + +
+

No blog posts yet

+

+ Stay tuned for upcoming articles about web development, programming, and technology. +

+
+
+
+ )} +
+ ); +} diff --git a/app/certifications/FrontendDeveloperReactCertificate.svg b/app/certifications/FrontendDeveloperReactCertificate.svg deleted file mode 100644 index f1c69a6..0000000 --- a/app/certifications/FrontendDeveloperReactCertificate.svg +++ /dev/null @@ -1,42 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/app/certifications/JavaBasicCertificate.svg b/app/certifications/JavaBasicCertificate.svg deleted file mode 100644 index b5e75c1..0000000 --- a/app/certifications/JavaBasicCertificate.svg +++ /dev/null @@ -1,42 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/app/certifications/ProblemSolvingBasicCertificate.svg b/app/certifications/ProblemSolvingBasicCertificate.svg deleted file mode 100644 index c0efb83..0000000 --- a/app/certifications/ProblemSolvingBasicCertificate.svg +++ /dev/null @@ -1,42 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/app/certifications/PythonBasicCertificate.svg b/app/certifications/PythonBasicCertificate.svg deleted file mode 100644 index 4ab866a..0000000 --- a/app/certifications/PythonBasicCertificate.svg +++ /dev/null @@ -1,42 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/app/certifications/ReactBasicCertificate.svg b/app/certifications/ReactBasicCertificate.svg deleted file mode 100644 index be15833..0000000 --- a/app/certifications/ReactBasicCertificate.svg +++ /dev/null @@ -1,42 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/app/experience/Experience.jsx b/app/experience/Experience.jsx deleted file mode 100644 index d4c7a02..0000000 --- a/app/experience/Experience.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; -import { Typography, Box, Divider, List, ListItem, Chip, ListItemText, Card, CardContent } from "@mui/material"; -import { Timeline, TimelineItem, TimelineSeparator, TimelineConnector, TimelineContent, TimelineDot } from "@mui/lab"; -import TimelineOppositeContent, { timelineOppositeContentClasses } from '@mui/lab/TimelineOppositeContent'; -import { Icons } from '../icons' - -export default function Experience({ experiences }) { - return ( - - - {experiences.map((info, idx) => { - return ( - - - {info.start.month} {info.start.year} - {info.end.month} {info.end.year} - - - - - - - - - - {info.role} | {info.company} - - - - - {info.notes.map((note, idx) => { - return ( - - - {note} - - - ) - })} - - - - {info.skills.map((skill) => { - return ( - - ) - })} - - - - - - ) - })} - - - ) -} \ No newline at end of file diff --git a/app/experience/ExperienceMobile.jsx b/app/experience/ExperienceMobile.jsx deleted file mode 100644 index 5472ddc..0000000 --- a/app/experience/ExperienceMobile.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from "react"; -import { Typography, Box, Divider, List, ListItem, Chip, ListItemText, Card, CardContent } from "@mui/material"; -import Grid from '@mui/material/Grid' -import { Icons } from '../icons' - -export default function ExperienceMobile({ experiences }) { - return ( - - {experiences.map((info, idx) => { - return ( - - - - - {info.role} - - - {info.company} - - - {info.start.month}, {info.start.year} - {info.end.month}, {info.end.year} - - - {info.org} - - - - {info.notes.map((note, idx) => { - return ( - - {note} - ) - })} - - - - {info.skills.map((skill) => { - return ( - - ) - })} - - - - ) - })} - - ) -} \ No newline at end of file diff --git a/app/experience/page.js b/app/experience/page.js deleted file mode 100644 index f1a8209..0000000 --- a/app/experience/page.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * List of experiences as objects - * - * Template: - * ```js - * { - * company: string, - * role: string, - * start: {month: string, year: number}, - * end: {month: string, year: number}, - * notes: string[], - * skills: string[] - * } - * ``` - */ - -'use client' - -import { useEffect, useState } from "react"; -import Experience from "./Experience"; -import ExperienceMobile from "./ExperienceMobile"; -import { Container, Box, Typography } from "@mui/material"; - -const experiences = [ - { - company: "Sphyrna Security Inc.", - role: "Research & Development", - start: { month: "May", year: 2023 }, - end: { month: "August", year: 2023 }, - notes: [ - "Explored secure communications methods, such as Wireguard, SSH, and SFTP", - "Developed software for a prototype secure communication device", - "Researched device and Linux Kernel hardening best practices", - ], - skills: ["C++", "Linux", "WireGuard", "Git"] - }, - { - company: "Sphyrna Security Inc.", - role: "Web Developer", - start: { month: "January", year: 2022 }, - end: { month: "December", year: 2022 }, - notes: [ - "Developed a full stack React/NodeJS solution to load the front-end web application in the user's preferred language, which increased our customer adoption rate as we could deploy to more regions", - "Implemented a secure authentication system using JWT, web cookies, and multi-factor authentication", - "Followed up on customer reported issues through GitLab" - ], - skills: ["ReactJS", "NodeJS", "ONgDB", "Bootstrap", "Git"] - }, - { - company: "Sphyrna Security Inc.", - role: "Research & Development", - start: { month: "May", year: 2021 }, - end: { month: "August", year: 2021 }, - notes: [ - "Collaborated with colleagues to develop prototype software in C/C++, to determine viability as products", - "Conducted research on Python attribute-based encryption implementations for use with our secure network products", - ], - skills: ["C++", "Python", "Git"] - }, - { - company: "Wize Computing Academy", - role: "Summer Intern", - start: { month: "July", year: 2020 }, - end: { month: "August", year: 2020 }, - notes: [ - "Designed lesson plans to teach coding skills for students aged 6 and up", - ], - skills: ["Google Docs", "Python", "Scratch", "LabView"] - }, -]; - -export default function Page() { - const [isDesktop, setIsDesktop] = useState(true); - - useEffect(() => { - if (window.innerWidth > 900) { - setIsDesktop(true); - } else { - setIsDesktop(false); - } - - const updateMedia = () => { - if (window.innerWidth > 900) { - setIsDesktop(true); - } else { - setIsDesktop(false); - } - }; - - window.addEventListener("resize", updateMedia); - return () => window.removeEventListener("resize", updateMedia); - }, []) - - return ( - - - - Experience - - {isDesktop ? () : ()} - - ) -} \ No newline at end of file diff --git a/app/experience/page.tsx b/app/experience/page.tsx new file mode 100644 index 0000000..2833cd3 --- /dev/null +++ b/app/experience/page.tsx @@ -0,0 +1,180 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Separator } from "@/components/ui/separator"; +import { CalendarDays, MapPin, ExternalLink } from "lucide-react"; +import Link from "next/link"; + +interface Experience { + id: string; + title: string; + company: string; + location: string; + startDate: string; + endDate?: string; + description: string; + achievements: string[]; + technologies: string[]; + website?: string; +} + +// You can easily extend this array to add more experiences +const experiences: Experience[] = [ + { + id: "1", + title: "Senior Full Stack Developer", + company: "Tech Solutions Inc.", + location: "San Francisco, CA", + startDate: "2022-01", + endDate: undefined, // Current position + description: "Leading development of scalable web applications and mentoring junior developers.", + achievements: [ + "Architected and implemented a microservices-based platform serving 1M+ users", + "Reduced application load time by 40% through optimization and caching strategies", + "Led a team of 5 developers and established code review processes", + "Implemented CI/CD pipelines reducing deployment time by 60%" + ], + technologies: ["React", "Node.js", "TypeScript", "PostgreSQL", "AWS", "Docker"], + website: "https://techsolutions.com" + }, + { + id: "2", + title: "Full Stack Developer", + company: "Digital Innovations", + location: "New York, NY", + startDate: "2020-06", + endDate: "2021-12", + description: "Developed and maintained multiple client projects using modern web technologies.", + achievements: [ + "Built 10+ responsive web applications for various clients", + "Collaborated with design team to implement pixel-perfect UIs", + "Integrated third-party APIs and payment gateways", + "Maintained 99% uptime for production applications" + ], + technologies: ["React", "Next.js", "Python", "Django", "MySQL", "Heroku"], + website: "https://digitalinnovations.com" + }, + { + id: "3", + title: "Frontend Developer", + company: "StartupXYZ", + location: "Austin, TX", + startDate: "2019-01", + endDate: "2020-05", + description: "Focused on creating intuitive user interfaces and improving user experience.", + achievements: [ + "Developed responsive web applications using React and Vue.js", + "Improved website performance resulting in 25% increase in user engagement", + "Collaborated with UX designers to implement accessibility features", + "Created reusable component library adopted across multiple projects" + ], + technologies: ["React", "Vue.js", "JavaScript", "SASS", "Webpack", "Jest"], + website: "https://startupxyz.com" + } +]; + +export default function ExperiencePage() { + return ( +
+
+

+ Professional Experience +

+

+ My journey through various roles and companies, building innovative solutions and growing as a developer. +

+
+ +
+ {experiences.map((experience, index) => ( + + +
+
+ {experience.title} + + + {experience.company} + {experience.website && ( + + + + )} + + + + {experience.location} + + +
+
+ + + {new Date(experience.startDate).toLocaleDateString('en-US', { + month: 'short', + year: 'numeric' + })} - { + experience.endDate + ? new Date(experience.endDate).toLocaleDateString('en-US', { + month: 'short', + year: 'numeric' + }) + : 'Present' + } + +
+
+
+ +

{experience.description}

+ +
+

Key Achievements:

+
    + {experience.achievements.map((achievement, idx) => ( +
  • {achievement}
  • + ))} +
+
+ +
+

Technologies Used:

+
+ {experience.technologies.map((tech, idx) => ( + + {tech} + + ))} +
+
+
+ {index < experiences.length - 1 && ( +
+ +
+ )} +
+ ))} +
+ + + + Want to work together? + + I'm always interested in new opportunities and collaborations. + + + +

+ Feel free to reach out if you'd like to discuss potential opportunities or just want to connect. + You can find my contact information in the footer or reach out through my social media profiles. +

+
+
+
+ ); +} diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/globals.css b/app/globals.css index d4f491e..a4839f2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,107 +1,186 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + :root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', - 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', - 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; - - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; - - --primary-glow: conic-gradient( - from 180deg at 50% 50%, - #16abff33 0deg, - #0885ff33 55deg, - #54d6ff33 120deg, - #0071ff33 160deg, - transparent 360deg - ); - --secondary-glow: radial-gradient( - rgba(255, 255, 255, 1), - rgba(255, 255, 255, 0) - ); - - --tile-start-rgb: 239, 245, 249; - --tile-end-rgb: 228, 232, 233; - --tile-border: conic-gradient( - #00000080, - #00000040, - #00000030, - #00000020, - #00000010, - #00000010, - #00000080 - ); - - --callout-rgb: 238, 240, 241; - --callout-border-rgb: 172, 175, 176; - --card-rgb: 180, 185, 188; - --card-border-rgb: 131, 134, 135; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - - --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); - --secondary-glow: linear-gradient( - to bottom right, - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0.3) - ); - - --tile-start-rgb: 2, 13, 46; - --tile-end-rgb: 2, 5, 19; - --tile-border: conic-gradient( - #ffffff80, - #ffffff40, - #ffffff30, - #ffffff20, - #ffffff10, - #ffffff10, - #ffffff80 - ); - - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; + --radius: 0.65rem; + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.705 0.213 47.604); + --primary-foreground: oklch(0.98 0.016 73.684); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.705 0.213 47.604); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.705 0.213 47.604); + --sidebar-primary-foreground: oklch(0.98 0.016 73.684); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.705 0.213 47.604); +} + +.dark { + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.646 0.222 41.116); + --primary-foreground: oklch(0.98 0.016 73.684); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.646 0.222 41.116); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.646 0.222 41.116); + --sidebar-primary-foreground: oklch(0.98 0.016 73.684); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.646 0.222 41.116); +} + + + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; } } -* { - box-sizing: border-box; - padding: 0; - margin: 0; +/* Syntax highlighting for code blocks */ +.prose pre { + @apply bg-muted border rounded-lg p-4 overflow-x-auto; } -html, -body { - max-width: 100vw; - overflow-x: hidden; +.prose code { + @apply bg-muted px-1 py-0.5 rounded text-sm; } -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); +.prose pre code { + @apply bg-transparent p-0; } -a { - color: inherit; - text-decoration: none; +/* Better spacing for prose content */ +.prose h1 { + @apply text-3xl font-bold mb-4 mt-8 first:mt-0; } -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } +.prose h2 { + @apply text-2xl font-semibold mb-3 mt-6 border-b border-border pb-2; +} + +.prose h3 { + @apply text-xl font-semibold mb-2 mt-4; +} + +.prose p { + @apply mb-4 leading-relaxed; +} + +.prose ul, .prose ol { + @apply mb-4 ml-6; +} + +.prose li { + @apply mb-1; +} + +.prose blockquote { + @apply border-l-4 border-primary pl-4 italic my-4; +} + +.prose a { + @apply text-primary hover:underline; +} + +.prose img { + @apply rounded-lg my-4; +} + +.prose table { + @apply w-full border-collapse border border-border my-4; +} + +.prose th, .prose td { + @apply border border-border p-2 text-left; +} + +.prose th { + @apply bg-muted font-semibold; } diff --git a/app/icons.js b/app/icons.js deleted file mode 100644 index 74f77e3..0000000 --- a/app/icons.js +++ /dev/null @@ -1,118 +0,0 @@ -import { - SiCplusplus, SiNodedotjs, SiOpenjdk, SiJavascript, SiReact, - SiDocker, SiNeo4J, SiCisco, SiPandas, SiPython, SiScratch, - SiLabview, SiSlack, SiFigma, SiGoogledrive, SiGraphql, - SiJunipernetworks, SiKubernetes, SiGit, - SiLinux, SiFedora, SiPopos, SiKalilinux, SiRaspberrypi, - SiTruenas, SiUbuntu, SiHp, SiBootstrap, SiFirst, SiDevpost, SiMui, SiNokia, SiPhp, - SiWireguard, SiFlask, SiNumpy, SiArduino, SiHtml5, SiCss3, SiNextdotjs -} from 'react-icons/si' -import { VscSymbolStructure, VscCircuitBoard, VscDatabase } from 'react-icons/vsc' -import { TbRouter } from 'react-icons/tb' -import { Diversity3, Diversity2, PermPhoneMsg, HourglassTop, GitHub } from '@mui/icons-material' - -export const Icons = { - "java": , - "c++": , - "c/c++": , - "nodejs": , - "ongdb": , - "javascript": , - "reactjs": , - "docker": , - 'cisco ios': , - "pandas": , - "python": , - 'scratch': , - "labview": , - "slack": , - "figma": , - "data structures": , - "google docs": , - "graphql": , - "juniper": , - "kubernetes": , - "routeros": , - 'teamwork': , - "agile development": , - "communication": , - "time management": , - "git": , - "linux": , - "popos": , - "kali": , - "fedora": , - "raspberrypi": , - "truenas": , - "ubuntu": , - "aruba": , - "bootstrap": , - "first": , - "embedded systems": , - "devpost": , - "github": , - "material ui": , - "nokia": , - "php": , - "wireguard": , - "flask": , - "numpy": , - "arduino": , - "sql": , - "html": , - "css": , - "nextjs": -} - -export const IconsLarge = { - "java": , - "c++": , - "c/c++": , - "nodejs": , - "ongdb": , - "javascript": , - "reactjs": , - "docker": , - 'cisco ios': , - "pandas": , - "python": , - 'scratch': , - "labview": , - "slack": , - "figma": , - "data structures": , - "google docs": , - "graphql": , - "juniper": , - "kubernetes": , - "routeros": , - 'teamwork': , - "agile development": , - "communication": , - "time management": , - "git": , - "linux": , - "popos": , - "kali": , - "fedora": , - "raspberrypi": , - "truenas": , - "ubuntu": , - "aruba": , - "bootstrap": , - "first": , - "embedded systems": , - "devpost": , - "github": , - "material ui": , - "nokia": , - "php": , - "wireguard": , - "flask": , - "numpy": , - "arduino": , - "sql": , - "html": , - "css": , - "nextjs": -} \ No newline at end of file diff --git a/app/layout.js b/app/layout.js deleted file mode 100644 index b5061d6..0000000 --- a/app/layout.js +++ /dev/null @@ -1,86 +0,0 @@ -import { Inter } from 'next/font/google' -import { ThemeProvider, CssBaseline, Typography, Link, Box, Container } from '@mui/material' -import Grid from '@mui/material/Grid' -import myTheme from './theme' -import './globals.css' -import NavBar from './navbar' -import { Icons } from './icons' - -const inter = Inter({ subsets: ['latin'] }) - -export const metadata = { - title: { - default: 'Sayfullah Eid', - template: '%s | Sayfullah Eid', - }, - description: 'Portfolio of Sayfullah Eid', - keywords: ['Sayfullah', 'Eid', 'Sayfullah Eid', 'Portfolio', 'Resume', 'CV', 'Carleton', 'Carleton University', 'CarletonU', 'Carleton University Alumni', 'Carleton Alumni', 'CarletonU Alumni', 'Carleton University Alumni Network', 'Carleton Alumni Network', 'CarletonU'], - authors: [{ name: 'Sayfullah Eid', email: 'sayfullaheid@gmail.com' }], - creator: 'Sayfullah Eid', - icons: { - icon: '/favicon.ico', - apple: '/logo192.png', - shortcut: '/logo192.png', - }, - robots: { - index: false, - follow: true, - nocache: true, - googleBot: { - index: true, - follow: false, - noimageindex: true, - 'max-video-preview': -1, - 'max-image-preview': 'large', - 'max-snippet': -1, - }, - }, - verification: { - other: { - me: ['https://fosstodon.org/@FusionStreak'], - }, - }, -} - -export default function RootLayout({ children }) { - return ( - - - - - - -
- {children} -
- - - - - - Bulit with {Icons["nextjs"]} Next.js and {Icons["material ui"]} Material UI - - - - - {'Copyright © '} - - Sayfullah Eid - {' '} - {new Date().getFullYear()} - {'.'} - - - - - Icons from React Icons - - - - - -
- - - ) -} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..9dc5ebf --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,74 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; +import { ThemeProvider } from "./theme-provider"; +import { NavigationBar } from "@/components/nav-menu"; +import Link from "next/link"; +import 'highlight.js/styles/github-dark.css'; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Sayfullah Eid", + description: "A personal Blog", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + +
+
+
+
+ + Sayfullah Eid + +
+
+ +
+
+
+
+ {children} +
+
+

+ © {new Date().getFullYear()} Created by{" "} + + Sayfullah Eid + {" "} +

+
+
+
+ + + ); +} diff --git a/app/navbar.jsx b/app/navbar.jsx deleted file mode 100644 index 7316d42..0000000 --- a/app/navbar.jsx +++ /dev/null @@ -1,224 +0,0 @@ -'use client' - -import React from "react"; -import { AppBar, Toolbar, Typography, IconButton, Menu, MenuItem, Box, Button, Tooltip, Divider, Container } from "@mui/material"; -import { SiMastodon } from 'react-icons/si' -import { GitHub, LinkedIn, Email, Download, Menu as MenuIcon } from '@mui/icons-material' -import Link from "next/link"; -import Image from "next/image"; -import { usePathname } from "next/navigation"; - -const logo = '/icon.png' - -export default function NavBar(props) { - - const [anchorElNav, setAnchorElNav] = React.useState(false); - - const handleOpenNavMenu = (event) => { - setAnchorElNav(event.currentTarget); - }; - - const handleCloseNavMenu = () => { - setAnchorElNav(false); - }; - - const pages = [ - { "route": "/", name: "Home" }, - { "route": "/experience", "name": "Experience" }, - { "route": "/projects", "name": "Projects" }, - ] - - const pathname = usePathname() - - return ( - - - - - - - - - - - {pages.map((page, idx) => ( - - ))} - - - - - - - - - - - - - - - - - - - - - - - {pages.map((page) => ( - - ))} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - logo - Sayfullah Eid - - - - - ) -} \ No newline at end of file diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..8be5cc9 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,38 @@ +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import Link from "next/link"; +import { Home, ArrowLeft } from "lucide-react"; + +export default function NotFound() { + return ( +
+ + +
+ 404 +
+ Page Not Found + + The page you're looking for doesn't exist or has been moved. + +
+ +
+ + +
+
+
+
+ ); +} diff --git a/app/page.js b/app/page.js deleted file mode 100644 index 51b4ec5..0000000 --- a/app/page.js +++ /dev/null @@ -1,212 +0,0 @@ -'use client' - -import Image from 'next/image' -import React from "react"; -import { Container, Typography, Box, Card, CardContent, Divider, Chip, CardActionArea, Dialog, DialogActions, DialogTitle, DialogContent, Button } from "@mui/material"; -import Grid from '@mui/material/Grid' -import { Icons } from "./icons"; -import { ArrowForward } from '@mui/icons-material'; - -const ACDark = '/ACDark.png' -const CarletonDark = '/CarletonDark.png' - -const carleton = { - 'SYSC 1005': { - 'title': 'Intro to Software Development', - 'description': 'Introduction to programming using Python. Topics include data types, control structures, functions, and object-oriented programming.', - 'skills': ['Python', 'Object-Oriented Programming'] - }, - 'SYSC 2006': { - 'title': 'Foundation of Imperative Programming', - 'description': 'The imperative programming paradigm: assignment and state, types and variables, static and dynamic typing. Control structures, functions, and parameter passing.', - 'skills': ['C/C++'] - }, - 'BIT 1400': { - 'title': 'Introduction to Programming and Problem Solving', - 'description': 'Introduction to basic concepts of procedural programming and algorithm design in C.', - 'skills': ['C/C++'] - }, - 'NET 2007': { - 'title': 'Basics of Transmission Systems', - 'description': 'Introduction to the fundamentals of information transmissions systems used in physical layer of the Internet.', - 'skills': ['Networking'] - }, - 'NET 2013': { - 'title': 'Computer Systems Foundations', - 'description': 'Introduction to the design and implementation of digital circuits and microprocessors.', - 'skills': ['Digital Circuits', 'Microprocessors', "Assembly"] - }, - 'BIT 2400': { - 'title': 'Intermediate Programming', - 'description': 'Introduction to object-oriented programming and algorithm design in C++.', - 'skills': ['C/C++'] - }, - 'NET 3004': { - 'title': 'Data Structures', - 'description': 'Specification and design of abstract data types and their implementation as stacks, queues, trees, tables and graphs.', - 'skills': ['Data Structures', 'Java'] - }, -} - -function Skills() { - const skills = { - 'Languages': ["Python", "Java", "C/C++", "JavaScript", "PHP"], - 'Libraries': ["ReactJS", "NodeJS", "GraphQL", "Bootstrap", "Material UI", "Pandas", "NumPy", "Arduino", "Flask"], - 'Networking': ["Cisco IOS", "Juniper", "Aruba", "RouterOS"] - } - - return ( - - - Skills - - - {Object.keys(skills).map((skill, idx) => { - return ( - - - {skill}: - - - {skills[skill].map((s, idx) => { - return ( - - ) - })} - - - ) - })} - - - - ) -} - -export default function Home() { - const [open, setOpen] = React.useState('none'); - - const handleClickOpen = (modal) => { - setOpen(modal); - }; - - const handleClose = () => { - setOpen('none'); - }; - - return ( - - - Sayfullah Eid - - - - - - - - Experience - - Internships & Professional Experience - - - - - - - - - - Projects - - Hackathons, Personal & Course Projects - - - - - - - - { handleClickOpen('carleton') }}> - - - - Bachelor of Information Technology - Network Technology - - - Carleton University Logo - - - - - Bachelor of Information Technology - - - Network Technology - - - Carleton University Logo - - - Expected Graduation: April 2024 - - - - - - - - { handleClickOpen('algonquin') }}> - - - Advanced Diploma in Computer Engineering Technology - Network Technology - - - Algonquin College Logo - - - - - Advanced Diploma in Computer Engineering Technology - - - Network Technology - - - Algonquin College Logo - - - Expected graduation: April 2024 - - - - - - - - Carleton University - - Bachelor of Information Technology - Network Technology - Expected Graduation: April 2024 - - - - - - - Algonquin College - - Advanced Diploma in Computer Engineering Technology - Network Technology - Expected graduation: April 2024 - - - - - - - ) -} diff --git a/app/page.module.css b/app/page.module.css deleted file mode 100644 index 6676d2c..0000000 --- a/app/page.module.css +++ /dev/null @@ -1,229 +0,0 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } -} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..ed6a110 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,127 @@ +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import Link from "next/link"; +import { Github, Linkedin, Mail, ExternalLink } from "lucide-react"; + +export default function HomePage() { + return ( +
+ {/* Hero Section */} +
+
+

+ Hello, I'm{" "} + Sayfullah Eid +

+

+ A passionate full-stack developer who loves building innovative solutions and sharing knowledge through code. +

+
+ +
+ + +
+ + {/* Social Links */} +
+ + + +
+
+ + {/* Skills Section */} + + + Technical Skills + + Technologies and tools I work with + + + +
+
+

Frontend

+
+ React + Next.js + TypeScript + Tailwind CSS +
+
+
+

Backend

+
+ Node.js + Python + PostgreSQL + MongoDB +
+
+
+

Tools & DevOps

+
+ Git + Docker + AWS + Vercel +
+
+
+
+
+ + {/* Quick Links */} +
+ + + Latest Projects + + Check out my recent work and side projects + + + + + + + + + + Professional Experience + + Learn about my professional journey and roles + + + + + + +
+
+ ); +} diff --git a/app/projects/Projects.jsx b/app/projects/Projects.jsx deleted file mode 100644 index d8ef0d4..0000000 --- a/app/projects/Projects.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from "react"; -import { Container, Typography, Box, Divider, List, ListItem, Chip, Card, CardContent, ListItemText, CardActions, Button, Tooltip } from "@mui/material"; -import { EmojiEvents, ChevronRight } from "@mui/icons-material"; -import Grid from "@mui/material/Grid"; -import { Icons } from "../icons"; - -export default function Projects({ projects }) { - - return ( - - {projects.map((info, idx) => { - return ( - - - - - {info.name} | {info.role} - - - { - info.date ? {info.date.month} {info.date.year} : {info.start.month} {info.start.year} - {info.end.month} {info.end.year} - } - - - {info.org} - - - - - {Object.keys(info.awards).map((award) => { - return } label={award} color="warning" variant="outlined" /> - })} - - {info.notes.map((note) => { - return ( - - {note} - ) - })} - - - - - {info.skills.map((skill) => { - return ( - - ) - })} - - - - {Object.keys(info.urls).length !== 0 ? - - {Object.keys(info.urls).map((url) => { - return ( - - ) - })} - - - : null} - - ) - })} - - ) -} \ No newline at end of file diff --git a/app/projects/page.js b/app/projects/page.js deleted file mode 100644 index 6c72d29..0000000 --- a/app/projects/page.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * List of project as objects - * - * Template - * ```js - * { - * name: string, - * role: string, - * notes: string[], - * urls: {name: string,...}, - * date: {month: string, year: number}, - * org: string, - * skills: string[], - * awards: string[] - * } - * ``` - */ - -'use client' - -import { useEffect, useState } from "react"; -import Projects from "./Projects"; -import { Container, Box, Typography } from "@mui/material"; - -const projects = [ - { - name: "LEO Satelite Routing and Load Balancing", - role: "Team Lead", - notes: [ - "Designing new load-balancing mechanisms for use in Low Eart Orbit networks", - "Extending the functionality of LEOSIM, a Python platform for simulating LEO networks", - "Implementing load-balancing designs in LEOSIM" - ], - urls: {}, - start: { month: "September", year: 2023 }, - end: { month: "April", year: 2024 }, - org: "Capstone Project | Carleton University", - skills: ["Python", "NumPy", "Docker", "Git"], - awards: {} - }, - { - name: "GoodStreams", - role: "Team Lead", - notes: [ - "Designed general structure of the website", - "Implemented backend functionality to communicate with MoviesDatabase API", - "Designed the database to store user information" - ], - urls: { - "GitHub": "https://github.com/FusionStreak/GoodStreams" - }, - date: { month: "April", year: 2023 }, - org: "Web Programming Project | Algonquin College", - skills: ["php", "javascript", "Git", "HTML", "CSS"], - awards: {} - }, - { - name: "foodpad", - role: "Frontend Developer", - notes: [ - "Implemented a prototype application to help track groceries and food waste, using React", - "Developed the front-end of the demo site, based on the designs from my colleagues." - ], - urls: { - "Devpost": "https://devpost.com/software/foodpad", - "GitHub": "https://github.com/ke-noel/foodpad" - }, - date: { month: "January", year: 2022 }, - org: "McHacks 9 | MLH", - skills: ["ReactJS", "Figma"], - awards: { "Top 5 Hacks": "One of the top 5 hacks at McHacks 9", "Tree Hugger": "Award for the hack that best promoted sustainability" } - }, - { - name: "PlagueSim", - role: "Lead Developer", - notes: [ - "Designed the general structure of the application through the use of UML diagrams", - "Implemented core data structures and functionality in Java" - ], - urls: { - "GitHub": "https://github.com/FusionStreak/MST_TermProject" - }, - date: { month: "December", year: 2021 }, - org: "Data Structures Project | Carleton University", - skills: ["Java", "Data Structures"], - awards: [] - }, - { - name: "harmonia", - role: "Frontend Developer", - notes: [ - "Developed a cognitive memory game using JavaScript that integrated with a Slack app, to perform daily check-ins on employee mental health" - ], - urls: { - "Devpost": "https://devpost.com/software/harmonia", - "GitHub": "https://github.com/banaru4/Harmonia" - }, - date: { month: "January", year: 2020 }, - org: "ConUHacks V | MLH", - skills: ["Javascript", "Slack"], - awards: { "Sun Life Financial": "Award for the app that best helped support mental health in the workplace" } - }, - { - name: "Orion", - role: "Developer", - notes: [ - "Developed Python web-app, that processed building footprint data to show where solar roofing was possible", - "Used Pandas to clean the incoming data" - ], - urls: { - "GitHub": "https://github.com/ogp-summit-hackathon-sommet-pgo/Orion" - }, - date: { month: "May", year: 2019 }, - org: "Ottawa Summit | Open Government Partnetship", - skills: ["Python", "Pandas", "Flask", "NumPy"], - awards: [] - }, - { - name: "RoboRavens", - role: "Programmer", - notes: [ - "Developed C/C++ code for a robot to complete a set of challenges" - ], - urls: {}, - date: { month: "January", year: 2018 }, - org: "Team 4783 | FIRST Robotics", - skills: ["FIRST", "C++", "Embedded Systems"], - awards: [] - } -] - -export default function Page() { - - return ( - - - - Projects - - - - ) -} \ No newline at end of file diff --git a/app/projects/page.tsx b/app/projects/page.tsx new file mode 100644 index 0000000..fc094e1 --- /dev/null +++ b/app/projects/page.tsx @@ -0,0 +1,260 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Github, ExternalLink, Calendar } from "lucide-react"; +import Link from "next/link"; + +interface Project { + id: string; + title: string; + description: string; + longDescription: string; + technologies: string[]; + githubUrl?: string; + liveUrl?: string; + imageUrl?: string; + featured: boolean; + createdAt: string; + status: "completed" | "in-progress" | "planning"; +} + +// You can easily extend this array to add more projects +const projects: Project[] = [ + { + id: "1", + title: "E-Commerce Platform", + description: "A full-stack e-commerce solution with admin dashboard and payment integration.", + longDescription: "Built a comprehensive e-commerce platform featuring user authentication, product catalog, shopping cart, order management, and Stripe payment integration. Includes an admin dashboard for inventory management and order tracking.", + technologies: ["Next.js", "TypeScript", "Prisma", "PostgreSQL", "Stripe", "Tailwind CSS"], + githubUrl: "https://github.com/yourusername/ecommerce-platform", + liveUrl: "https://your-ecommerce-demo.vercel.app", + imageUrl: "/placeholder-project-1.jpg", + featured: true, + createdAt: "2024-01-15", + status: "completed" + }, + { + id: "2", + title: "Task Management App", + description: "A collaborative task management application with real-time updates.", + longDescription: "Developed a collaborative task management application with features like drag-and-drop kanban boards, real-time collaboration, team management, and deadline tracking. Built with modern React patterns and real-time WebSocket communication.", + technologies: ["React", "Node.js", "Socket.io", "MongoDB", "Express", "Material-UI"], + githubUrl: "https://github.com/yourusername/task-manager", + liveUrl: "https://task-manager-demo.netlify.app", + imageUrl: "/placeholder-project-2.jpg", + featured: true, + createdAt: "2023-11-20", + status: "completed" + }, + { + id: "3", + title: "Weather Dashboard", + description: "A responsive weather dashboard with location-based forecasts and data visualization.", + longDescription: "Created a weather dashboard that provides current weather conditions, 5-day forecasts, and interactive charts. Features geolocation support, search functionality, and responsive design with dark/light theme support.", + technologies: ["React", "Chart.js", "OpenWeather API", "CSS Modules", "Axios"], + githubUrl: "https://github.com/yourusername/weather-dashboard", + liveUrl: "https://weather-dashboard-demo.vercel.app", + imageUrl: "/placeholder-project-3.jpg", + featured: false, + createdAt: "2023-09-10", + status: "completed" + }, + { + id: "4", + title: "AI Chat Application", + description: "An AI-powered chat application with multiple conversation models.", + longDescription: "Building an intelligent chat application that integrates with various AI models. Features include conversation history, multiple AI personalities, message export, and customizable chat interfaces.", + technologies: ["Next.js", "OpenAI API", "Vercel AI SDK", "Prisma", "SQLite", "Shadcn/ui"], + githubUrl: "https://github.com/yourusername/ai-chat-app", + imageUrl: "/placeholder-project-4.jpg", + featured: true, + createdAt: "2024-02-01", + status: "in-progress" + }, + { + id: "5", + title: "Portfolio Website", + description: "This very website - a modern portfolio built with Next.js and MDX.", + longDescription: "Designed and developed this portfolio website using Next.js, TypeScript, and MDX for the blog. Features include dark/light theme, responsive design, blog functionality, and optimized performance.", + technologies: ["Next.js", "TypeScript", "MDX", "Tailwind CSS", "Shadcn/ui"], + githubUrl: "https://github.com/FusionStreak/FusionStreak.github.io", + liveUrl: "https://fusionstreak.github.io", + imageUrl: "/placeholder-project-5.jpg", + featured: false, + createdAt: "2024-01-01", + status: "completed" + } +]; + +const statusColors = { + completed: "bg-green-500", + "in-progress": "bg-yellow-500", + planning: "bg-blue-500" +}; + +export default function ProjectsPage() { + const featuredProjects = projects.filter(project => project.featured); + const otherProjects = projects.filter(project => !project.featured); + + return ( +
+
+

+ My Projects +

+

+ A collection of projects I've worked on, showcasing my skills and passion for development. +

+
+ + {/* Featured Projects */} + {featuredProjects.length > 0 && ( +
+

Featured Projects

+
+ {featuredProjects.map((project) => ( + + {project.imageUrl && ( +
+ {/* Placeholder for project image */} +
+ Project Screenshot +
+
+ )} + +
+
+ + {project.title} +
+ + {project.description} +
+
+ + {new Date(project.createdAt).getFullYear()} +
+
+ + +

+ {project.longDescription} +

+ +
+ {project.technologies.slice(0, 4).map((tech, idx) => ( + + {tech} + + ))} + {project.technologies.length > 4 && ( + + +{project.technologies.length - 4} more + + )} +
+ +
+ {project.githubUrl && ( + + )} + {project.liveUrl && ( + + )} +
+
+ + ))} +
+
+ )} + + {/* Other Projects */} + {otherProjects.length > 0 && ( +
+

Other Projects

+
+ {otherProjects.map((project) => ( + + +
+ + {project.title} +
+ +
+ + {new Date(project.createdAt).getFullYear()} +
+
+ + {project.description} + + + +
+ {project.technologies.slice(0, 3).map((tech, idx) => ( + + {tech} + + ))} + {project.technologies.length > 3 && ( + + +{project.technologies.length - 3} + + )} +
+ +
+ {project.githubUrl && ( + + )} + {project.liveUrl && ( + + )} +
+
+ + ))} +
+
+ )} + + {/* Call to Action */} + + + Interested in collaborating? + + I'm always open to discussing new projects and opportunities. + + + + + + +
+ ); +} diff --git a/app/theme-provider.tsx b/app/theme-provider.tsx new file mode 100644 index 0000000..a32b909 --- /dev/null +++ b/app/theme-provider.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as React from "react" +import { ThemeProvider as NextThemesProvider } from "next-themes" + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children} +} diff --git a/app/theme.jsx b/app/theme.jsx deleted file mode 100644 index db9151f..0000000 --- a/app/theme.jsx +++ /dev/null @@ -1,72 +0,0 @@ -'use client' - -import { createTheme } from '@mui/material/styles'; -import { deepOrange, grey } from '@mui/material/colors'; - -const myTheme = createTheme({ - palette: { - mode: 'dark', - primary: { - main: deepOrange['900'] - }, - secondary: { - main: '#22cbff' - }, - error: { - main: '#ff225d' - }, - warning: { - main: '#ffc422' - }, - info: { - main: '#cbff22' - }, - success: { - main: '#22ff56' - }, - background: { - paper: grey[900], - default: '#122023' - } - }, - components: { - MuiChip: { - styleOverrides: { - root: { - marginRight: '0.75rem', - paddingLeft: '0.75rem', - marginTop: '0.75rem', - } - , - iconMedium: { - width: '1.5rem', - height: '1.5rem', - color: 'inherit' - }, - label: { - fontWeight: 'bold' - }, - colorPrimary: { - backgroundColor: deepOrange['900'] - } - } - }, - MuiTypography: { - styleOverrides: { - overline: { - color: deepOrange['A200'], - fontWeight: 600 - } - } - }, - MuiListItem: { - styleOverrides: { - root: { - padding: '4px', - } - } - } - } -}); - -export default myTheme; \ No newline at end of file diff --git a/components.json b/components.json new file mode 100644 index 0000000..5a3c750 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/components/mdx-components.tsx b/components/mdx-components.tsx new file mode 100644 index 0000000..0b757ac --- /dev/null +++ b/components/mdx-components.tsx @@ -0,0 +1,120 @@ +import type { MDXComponents } from 'mdx/types'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Separator } from '@/components/ui/separator'; + +export function useMDXComponents(components: MDXComponents): MDXComponents { + return { + h1: ({ children }) => ( +

+ {children} +

+ ), + h2: ({ children }) => ( +

+ {children} +

+ ), + h3: ({ children }) => ( +

+ {children} +

+ ), + h4: ({ children }) => ( +

+ {children} +

+ ), + p: ({ children }) => ( +

+ {children} +

+ ), + ul: ({ children }) => ( +
    + {children} +
+ ), + ol: ({ children }) => ( +
    + {children} +
+ ), + li: ({ children }) => ( +
  • + {children} +
  • + ), + blockquote: ({ children }) => ( +
    + {children} +
    + ), + code: ({ children }) => ( + + {children} + + ), + pre: ({ children }) => ( +
    +                {children}
    +            
    + ), + a: ({ href, children }) => ( + + {children} + + ), + hr: () => , + table: ({ children }) => ( +
    + + {children} +
    +
    + ), + thead: ({ children }) => ( + + {children} + + ), + tbody: ({ children }) => ( + + {children} + + ), + tr: ({ children }) => ( + + {children} + + ), + th: ({ children }) => ( + + {children} + + ), + td: ({ children }) => ( + + {children} + + ), + Card: ({ title, description, children, ...props }) => ( + + {(title || description) && ( + + {title && {title}} + {description && {description}} + + )} + {children} + + ), + Badge, + ...components, + }; +} diff --git a/components/mdx-content.tsx b/components/mdx-content.tsx new file mode 100644 index 0000000..07ec3c9 --- /dev/null +++ b/components/mdx-content.tsx @@ -0,0 +1,36 @@ +import { remark } from 'remark'; +import remarkGfm from 'remark-gfm'; +import remarkRehype from 'remark-rehype'; +import rehypeHighlight from 'rehype-highlight'; +import rehypeSlug from 'rehype-slug'; +import rehypeAutolinkHeadings from 'rehype-autolink-headings'; +import rehypeStringify from 'rehype-stringify'; + +export async function renderMDXToHTML(source: string): Promise { + const processor = remark() + .use(remarkGfm) + .use(remarkRehype, { allowDangerousHtml: true }) + .use(rehypeHighlight) + .use(rehypeSlug) + .use(rehypeAutolinkHeadings, { behavior: 'wrap' }) + .use(rehypeStringify, { allowDangerousHtml: true }); + + const result = await processor.process(source); + return String(result); +} + +interface MDXContentProps { + source: string; + className?: string; +} + +export async function MDXContent({ source, className = "prose prose-gray dark:prose-invert max-w-none" }: MDXContentProps) { + const htmlContent = await renderMDXToHTML(source); + + return ( +
    + ); +} diff --git a/components/mdx-renderer.tsx b/components/mdx-renderer.tsx new file mode 100644 index 0000000..3176a8c --- /dev/null +++ b/components/mdx-renderer.tsx @@ -0,0 +1,90 @@ +'use client'; + +import React from 'react'; +import { compile, run } from '@mdx-js/mdx'; +import * as runtime from 'react/jsx-runtime'; +import remarkGfm from 'remark-gfm'; +import rehypeHighlight from 'rehype-highlight'; +import rehypeSlug from 'rehype-slug'; +import rehypeAutolinkHeadings from 'rehype-autolink-headings'; +import { useMDXComponents } from '@/components/mdx-components'; +import 'highlight.js/styles/github-dark.css'; + +interface MDXRendererProps { + source: string; +} + +export function MDXRenderer({ source }: MDXRendererProps) { + const [MDXContent, setMDXContent] = React.useState> | null>(null); + const [error, setError] = React.useState(null); + const [isLoading, setIsLoading] = React.useState(true); + + React.useEffect(() => { + async function compileMDX() { + try { + setIsLoading(true); + setError(null); + + // Compile the MDX source + const compiled = await compile(source, { + outputFormat: 'function-body', + remarkPlugins: [remarkGfm], + rehypePlugins: [ + rehypeHighlight, + rehypeSlug, + [rehypeAutolinkHeadings, { behavior: 'wrap' }], + ], + }); + + // Run the compiled MDX to get the component + const { default: Component } = await run(compiled, { + ...runtime, + }); + + setMDXContent(() => Component); + } catch (err) { + console.error('Error compiling MDX:', err); + setError(err instanceof Error ? err.message : 'Unknown error occurred'); + } finally { + setIsLoading(false); + } + } + + compileMDX(); + }, [source]); + + const components = useMDXComponents({}); + + if (isLoading) { + return ( +
    +
    +
    + ); + } + + if (error) { + return ( +
    +

    + Error rendering content: +

    +

    + {error} +

    +
    + ); + } + + if (!MDXContent) { + return ( +
    +

    + No content to render. +

    +
    + ); + } + + return ; +} diff --git a/components/nav-menu.tsx b/components/nav-menu.tsx new file mode 100644 index 0000000..0744572 --- /dev/null +++ b/components/nav-menu.tsx @@ -0,0 +1,67 @@ +"use client" + +import * as React from "react" + +import { cn } from "@/lib/utils" +import { + NavigationMenu, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + navigationMenuTriggerStyle, +} from "@/components/ui/navigation-menu" + +export function NavigationBar() { + return ( + + + + + Home + + + + + Experience + + + + + Projects + + + + + Blog + + + + + ) +} + +const ListItem = React.forwardRef< + React.ComponentRef<"a">, + React.ComponentPropsWithoutRef<"a"> +>(({ className, title, children, ...props }, ref) => { + return ( +
  • + + +
    {title}
    +

    + {children} +

    +
    +
    +
  • + ) +}) +ListItem.displayName = "ListItem" diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..0205413 --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return