├── .eslintrc.json
├── .gitignore
├── README.md
├── components.json
├── next.config.mjs
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── public
├── assets
│ ├── logo.png
│ ├── profile.jpg
│ ├── project_1.jpeg
│ ├── project_2.jpeg
│ ├── project_3.jpeg
│ ├── testimonial_1_avatar.jpg
│ └── testimonial_2_avatar.jpg
├── data.json
├── next.svg
└── vercel.svg
├── src
├── app
│ ├── blogs
│ │ ├── how_the_blog_feature_works
│ │ │ └── page.mdx
│ │ ├── layout.tsx
│ │ └── welcome_to_logsfolio
│ │ │ └── page.mdx
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── components
│ └── ui
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── footer.tsx
│ │ ├── navbar.tsx
│ │ ├── sheet.tsx
│ │ └── themeToggler.tsx
├── lib
│ ├── serverUtils.ts
│ └── utils.ts
├── mdx-components.tsx
└── types
│ ├── blog.ts
│ └── data.ts
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Developer Portfolio Template
2 |
3 | Welcome to the Developer Portfolio Template! This open-source project allows you to quickly set up a personal portfolio website with minimal effort. The main data is sourced from a JSON file, making it easy to customize and update.
4 |
5 | ## Getting Started
6 |
7 | ### Prerequisites
8 |
9 | Ensure you have the following installed on your machine:
10 |
11 | - Node.js
12 | - npm or yarn or pnpm
13 | - Git
14 |
15 | ### Installation
16 |
17 | 1. **Clone the repository**
18 |
19 | ```sh
20 | git clone https://github.com/ariflogs/devfolio.git
21 | cd devfolio
22 | ```
23 |
24 | 2. **Install dependencies**
25 |
26 | ```sh
27 | npm i pnpm -g
28 | pnpm install
29 | ```
30 |
31 |
32 | 3. **Run the development server**
33 |
34 | ```sh
35 | pnpm run dev
36 | ```
37 |
38 | Open http://localhost:3000 with your browser to see the result.
39 |
40 |
41 | ### Customization
42 |
43 | 1. **Update data.json**
44 |
45 | The main data source for the portfolio is a JSON file located at ***public/data.json***. Edit this file to reflect your personal information, skills, experience, projects, and other sections.
46 |
47 |
48 | 2. **Styling**
49 |
50 | Tailwind CSS is used for styling. You can customize the styles in the tailwind.config.js file and add your own styles in styles/globals.css.
51 |
52 | ### Deployment
53 |
54 | You can deploy your Next.js application to various hosting platforms such as Vercel, Netlify, or any platform that supports Node.js applications.
55 |
56 |
57 | ## Contributing
58 | Contributions are welcome! If you have any improvements or suggestions, please create a pull request or open an issue.
59 |
60 | ## License
61 | This project is licensed under the MIT License.
62 |
63 | ---
64 |
65 | Happy coding! If you have any questions, feel free to open an issue or [reach out](https://twitter.com/ariflogs).
66 |
67 |
68 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "src/app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | import withMDX from '@next/mdx'
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
6 | };
7 |
8 | export default withMDX()(nextConfig)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "devfolio",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mdx-js/loader": "^3.0.1",
13 | "@mdx-js/react": "^3.0.1",
14 | "@next/mdx": "^14.2.4",
15 | "@radix-ui/react-avatar": "^1.0.4",
16 | "@radix-ui/react-dialog": "^1.0.5",
17 | "@radix-ui/react-icons": "^1.3.0",
18 | "@radix-ui/react-slot": "^1.0.2",
19 | "@types/mdx": "^2.0.13",
20 | "class-variance-authority": "^0.7.0",
21 | "clsx": "^2.1.1",
22 | "lucide-react": "^0.383.0",
23 | "next": "14.2.3",
24 | "react": "^18",
25 | "react-dom": "^18",
26 | "tailwind-merge": "^2.3.0",
27 | "tailwindcss-animate": "^1.0.7"
28 | },
29 | "devDependencies": {
30 | "@types/node": "^20",
31 | "@types/react": "^18",
32 | "@types/react-dom": "^18",
33 | "eslint": "^8",
34 | "eslint-config-next": "14.2.3",
35 | "postcss": "^8",
36 | "tailwindcss": "^3.4.1",
37 | "typescript": "^5"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/logo.png
--------------------------------------------------------------------------------
/public/assets/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/profile.jpg
--------------------------------------------------------------------------------
/public/assets/project_1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/project_1.jpeg
--------------------------------------------------------------------------------
/public/assets/project_2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/project_2.jpeg
--------------------------------------------------------------------------------
/public/assets/project_3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/project_3.jpeg
--------------------------------------------------------------------------------
/public/assets/testimonial_1_avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/testimonial_1_avatar.jpg
--------------------------------------------------------------------------------
/public/assets/testimonial_2_avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/public/assets/testimonial_2_avatar.jpg
--------------------------------------------------------------------------------
/public/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "visual": {
3 | "navbar": {
4 | "links": [
5 | {
6 | "label": "Experience",
7 | "path": "#experience"
8 | },
9 | {
10 | "label": "Projects",
11 | "path": "#projects"
12 | },
13 | {
14 | "label": "Education",
15 | "path": "#education"
16 | },
17 | {
18 | "label": "Testimonials",
19 | "path": "#testimonials"
20 | },
21 | {
22 | "label": "Blogs",
23 | "path": "#blogs"
24 | }
25 | ]
26 | },
27 | "home": {
28 | "sections": {
29 | "banner": true,
30 | "experience": true,
31 | "project": true,
32 | "education": true,
33 | "testimonial": true
34 | }
35 | }
36 | },
37 | "personalInfo": {
38 | "name": "Arif Hossain",
39 | "title": "Full Stack Developer",
40 | "location": "Dhaka, Bangladesh",
41 | "bio": "I am a passionate full-stack developer with a strong background in building modern, scalable web applications. Lets collaborate andbring your ideas to life."
42 | },
43 | "contactInfo": {
44 | "email": "devarifhossain@gmail.com",
45 | "phone": "123-456-7890",
46 | "linkedin": "https://www.linkedin.com/in/ariflogs/",
47 | "github": "https://github.com/ariflogs",
48 | "twitter": "https://twitter.com/ariflogs",
49 | "website": "https://johndoe.dev"
50 | },
51 | "skills": {
52 | "languages": ["JavaScript", "Python", "Java", "C++"],
53 | "frameworks": ["React", "Node.js", "Express", "Django", "Spring Boot"],
54 | "databases": ["MySQL", "MongoDB", "PostgreSQL"],
55 | "tools": ["Git", "Docker", "Kubernetes", "Jenkins", "AWS", "GCP"]
56 | },
57 | "projects": [
58 | {
59 | "id": "1",
60 | "title": "Makers Tracker",
61 | "description": "A project goal tracking tool for professionals. It let's you create project, assign goals and generate analytics to track progress",
62 | "technologies": ["Typescript", "Next.js", "TailwindCSS", "MySQL"],
63 | "live_url": "https://makerstracker.com",
64 | "code_repo_url": "https://makerstracker.com",
65 | "cover": "/assets/project_1.jpeg"
66 | },
67 | {
68 | "id": "2",
69 | "title": "Pricing Bees",
70 | "description": "A no-code pricing page builder. Build professional-looking pricing pages, customize looks, and get detailed analytics to optimize your pricing strategy.",
71 | "technologies": ["Typescript", "Next.js", "Shadcn/ui", "Convex"],
72 | "live_url": "https://pricingbees.com",
73 | "code_repo_url": "https://pricingbees.com",
74 | "cover": "/assets/project_2.jpeg"
75 | },
76 | {
77 | "id": "3",
78 | "title": "Indie Hustles",
79 | "description": "A listing site for Indie hackers to promote their products",
80 | "technologies": ["Typescript", "Next.js", "TailwindCSS", "Airtable"],
81 | "live_url": "https://indiehustles.com",
82 | "code_repo_url": "https://indiehustles.com",
83 | "cover": "/assets/project_3.jpeg"
84 | }
85 | ],
86 | "workExperience": [
87 | {
88 | "id": "1",
89 | "company": "Tech Corp",
90 | "companyWebsite": "https://example.com",
91 | "role": "Senior Full Stack Developer",
92 | "startDate": "Jan 2020",
93 | "endDate": "Present",
94 | "technologies": ["Socket.io", "Node.js", "Express", "React"],
95 | "keyResponsibilities": [
96 | "Led a team of developers to create scalable web applications.",
97 | "Implemented RESTful and GraphQL APIs using Node.js and Express.",
98 | "Developed front-end applications using React and Next.js.",
99 | "Integrated third-party services and APIs to enhance application functionality.",
100 | "Mentored junior developers and conducted code reviews."
101 | ]
102 | },
103 | {
104 | "id": "2",
105 | "company": "Web Solutions",
106 | "companyWebsite": "https://example.com",
107 | "role": "Full Stack Developer",
108 | "startDate": "Jun 2017",
109 | "endDate": "Dec 2019",
110 | "technologies": ["MongoDB", "Express", "React", "Node.js"],
111 | "keyResponsibilities": [
112 | "Designed and developed web applications using JavaScript and React.",
113 | "Built and maintained server-side APIs with Node.js and Express.",
114 | "Collaborated with cross-functional teams to deliver high-quality products.",
115 | "Ensured applications are responsive and performant.",
116 | "Participated in Agile ceremonies and contributed to project planning."
117 | ]
118 | }
119 | ],
120 | "education": [
121 | {
122 | "id": "1",
123 | "institution": "Coursera",
124 | "degree": "Full Stack Web Development with React",
125 | "description": "Completed a Coursera specialization that focused on building full-stack web applications using React, Node.js, Express, and MongoDB. Learned best practices for designing and implementing responsive, scalable, and secure web applications.",
126 | "startDate": "Sep 2013",
127 | "endDate": "May 2017"
128 | },
129 | {
130 | "id": "2",
131 | "institution": "University of California, Berkeley",
132 | "degree": "Bachelor of Science in Computer Science",
133 | "description": "Completed a comprehensive curriculum in computer science, including courses in data structures, algorithms, software engineering, and artificial intelligence. Worked on several projects, including a web application for managing student organizations and a mobile app for tracking fitness activities.",
134 | "startDate": "Sep 2013",
135 | "endDate": "May 2017"
136 | }
137 | ],
138 | "testimonials": [
139 | {
140 | "id": "1",
141 | "name": "Jane Smith",
142 | "avatar": "/assets/testimonial_1_avatar.jpg",
143 | "title": "Project Manager",
144 | "company": "Tech Solutions Inc.",
145 | "feedback": "John is a fantastic developer who consistently delivers high-quality work. His ability to solve complex problems is impressive."
146 | },
147 | {
148 | "id": "2",
149 | "name": "Mike Johnson",
150 | "avatar": "/assets/testimonial_2_avatar.jpg",
151 | "title": "CEO",
152 | "company": "Web Innovators LLC",
153 | "feedback": "John is a highly skilled developer who brings a lot of value to our projects. His expertise in full stack development is top-notch."
154 | }
155 | ]
156 | }
157 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/blogs/how_the_blog_feature_works/page.mdx:
--------------------------------------------------------------------------------
1 | export const metadata = {
2 | title: "How the Blog Feature Work...",
3 | description:
4 | "Learn how to use MDX and NextJS to add static blogs in your website",
5 | isPublished: true,
6 | slug: "welcome_to_logsfolio",
7 | publishDate: "2nd Jul, 2024",
8 | };
9 |
10 |
11 |
12 | # How the Blog Feature Work...
13 |
14 |
15 |
16 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/app/blogs/layout.tsx:
--------------------------------------------------------------------------------
1 | import { NextRequest } from "next/server";
2 |
3 | export default function BlogLayout({
4 | children,
5 | params,
6 | }: Readonly<{
7 | children: React.ReactNode;
8 | params: NextRequest;
9 | }>) {
10 | return {children}
;
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/blogs/welcome_to_logsfolio/page.mdx:
--------------------------------------------------------------------------------
1 | export const metadata = {
2 | title: "Welcome to LogsFolio Website! 🙌",
3 | description:
4 | "Although there are many open-source portfolio websites out there, but most of them are hard to customize! I created Logsfolio solves if for you.",
5 | isPublished: true,
6 | slug: "welcome_to_logsfolio",
7 | publishDate: "2nd Jul, 2024",
8 | };
9 |
10 |
11 |
12 | # Welcome to LogsFolio Website! 🙌
13 |
14 |
15 |
16 |
26 |
27 |
28 |
29 | This open-source project allows you to quickly set up a personal portfolio website with minimal effort. The main data is sourced from a JSON file, making it easy to customize and update.
30 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Logging-Stuff/logsfolio/3b42fd7b29dfef622ec82fd714a66e0e89c5d4ec/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | html {
6 | scroll-behavior: smooth;
7 | }
8 |
9 | @layer base {
10 | :root {
11 | --background: 0 0% 100%;
12 | --foreground: 224 71.4% 4.1%;
13 | --card: 0 0% 100%;
14 | --card-foreground: 224 71.4% 4.1%;
15 | --popover: 0 0% 100%;
16 | --popover-foreground: 224 71.4% 4.1%;
17 | --primary: 262.1 83.3% 57.8%;
18 | --primary-foreground: 210 20% 98%;
19 | --secondary: 220 14.3% 95.9%;
20 | --secondary-foreground: 220.9 39.3% 11%;
21 | --muted: 220 14.3% 95.9%;
22 | --muted-foreground: 220 8.9% 46.1%;
23 | --accent: 220 14.3% 95.9%;
24 | --accent-foreground: 220.9 39.3% 11%;
25 | --destructive: 0 84.2% 60.2%;
26 | --destructive-foreground: 210 20% 98%;
27 | --border: 220 13% 91%;
28 | --input: 220 13% 91%;
29 | --ring: 262.1 83.3% 57.8%;
30 | --radius: 0.5rem;
31 | }
32 |
33 | .dark {
34 | --background: 224 71.4% 4.1%;
35 | --foreground: 210 20% 98%;
36 | --card: 224 71.4% 4.1%;
37 | --card-foreground: 210 20% 98%;
38 | --popover: 224 71.4% 4.1%;
39 | --popover-foreground: 210 20% 98%;
40 | --primary: 263.4 70% 50.4%;
41 | --primary-foreground: 210 20% 98%;
42 | --secondary: 215 27.9% 16.9%;
43 | --secondary-foreground: 210 20% 98%;
44 | --muted: 215 27.9% 16.9%;
45 | --muted-foreground: 217.9 10.6% 64.9%;
46 | --accent: 215 27.9% 16.9%;
47 | --accent-foreground: 210 20% 98%;
48 | --destructive: 0 62.8% 30.6%;
49 | --destructive-foreground: 210 20% 98%;
50 | --border: 215 27.9% 16.9%;
51 | --input: 215 27.9% 16.9%;
52 | --ring: 263.4 70% 50.4%;
53 | }
54 | }
55 |
56 | .blog-post h1 {
57 | font-size: 2.5em;
58 | }
59 |
60 | .blog-post h2 {
61 | font-size: 2em;
62 | }
63 |
64 | .blog-post h3 {
65 | font-size: 1.75em;
66 | }
67 |
68 | .blog-post h4 {
69 | font-size: 1.5em;
70 | }
71 |
72 | .blog-post h5 {
73 | font-size: 1.25em;
74 | }
75 |
76 | .blog-post h6 {
77 | font-size: 1em;
78 | }
79 |
80 | .blog-post p {
81 | margin: 0 0 1.5em;
82 | }
83 |
84 | .blog-post a {
85 | color: #3498db;
86 | text-decoration: none;
87 | }
88 |
89 | .blog-post a:hover {
90 | text-decoration: underline;
91 | }
92 |
93 | .blog-post strong {
94 | font-weight: bold;
95 | }
96 |
97 | .blog-post em {
98 | font-style: italic;
99 | }
100 |
101 | .blog-post blockquote {
102 | font-style: italic;
103 | color: #666;
104 | margin: 1.5em 10px;
105 | padding: 0.5em 10px;
106 | border-left: 5px solid #ccc;
107 | }
108 |
109 | .blog-post ul, ol {
110 | margin: 0 0 1.5em 1.5em;
111 | }
112 |
113 | .blog-post li {
114 | margin-bottom: 0.5em;
115 | }
116 |
117 | .blog-post code {
118 | font-family: 'Courier New', Courier, monospace;
119 | background-color: #f4f4f4;
120 | padding: 2px 4px;
121 | border-radius: 4px;
122 | color: #c7254e;
123 | }
124 |
125 | .blog-post pre {
126 | font-family: 'Courier New', Courier, monospace;
127 | background-color: #f4f4f4;
128 | padding: 10px;
129 | border-radius: 4px;
130 | overflow: auto;
131 | }
132 |
133 | .blog-post img {
134 | max-width: 100%;
135 | height: auto;
136 | }
137 |
138 | .blog-post figure {
139 | margin: 0 0 1.5em;
140 | }
141 |
142 | .blog-post figcaption {
143 | font-size: 0.9em;
144 | color: #666;
145 | text-align: center;
146 | }
147 |
148 | .blog-post hr {
149 | border: 0;
150 | height: 1px;
151 | background: #ccc;
152 | margin: 2em 0;
153 | }
154 |
155 | @media (max-width: 768px) {
156 | .blog-post h1 {
157 | font-size: 2em;
158 | }
159 |
160 | .blog-post h2 {
161 | font-size: 1.75em;
162 | }
163 |
164 | .blog-post h3 {
165 | font-size: 1.5em;
166 | }
167 |
168 | .blog-post h4 {
169 | font-size: 1.25em;
170 | }
171 |
172 | .blog-post h5 {
173 | font-size: 1em;
174 | }
175 |
176 | .blog-post h6 {
177 | font-size: 0.875em;
178 | }
179 | }
180 |
181 | body.dark {
182 | background: #000;
183 | color: #aeaeae;
184 | }
185 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 | import Footer from "@/components/ui/footer";
5 | import Navbar from "@/components/ui/navbar";
6 |
7 | const inter = Inter({ subsets: ["latin"] });
8 |
9 | export const metadata: Metadata = {
10 | title: "Logsfolio | Developer Portfolio",
11 | description:
12 | "A clean looking FREE portfolio template for devs. Built with NextJS & TailwindCSS",
13 | };
14 |
15 | export default function RootLayout({
16 | children,
17 | }: Readonly<{
18 | children: React.ReactNode;
19 | }>) {
20 | return (
21 |
22 |
23 |
24 |
25 | {children}
26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @next/next/no-img-element */
2 | import { Badge } from "@/components/ui/badge";
3 | import { Button } from "@/components/ui/button";
4 | import {
5 | Card,
6 | CardContent,
7 | CardDescription,
8 | CardFooter,
9 | CardHeader,
10 | CardTitle,
11 | } from "@/components/ui/card";
12 | import { getBlogPosts, getJSONData } from "@/lib/serverUtils";
13 | import Link from "next/link";
14 | import {
15 | EnvelopeClosedIcon,
16 | GitHubLogoIcon,
17 | LinkedInLogoIcon,
18 | TwitterLogoIcon,
19 | GlobeIcon,
20 | } from "@radix-ui/react-icons";
21 | import { Avatar } from "@/components/ui/avatar";
22 | import Image from "next/image";
23 |
24 | export default async function Home() {
25 | const data = await getJSONData();
26 | const posts = await getBlogPosts();
27 |
28 | return (
29 |
30 | {/* Banner Section */}
31 |
35 |
36 |
37 |
44 |
45 |
46 |
47 |
48 | Hey 👋, I'm {data.personalInfo.name}
49 |
50 |
51 |
52 | {data.personalInfo.bio}
53 |
54 |
55 |
60 |
63 |
64 |
69 |
72 |
73 |
74 |
79 |
82 |
83 |
84 |
85 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | {/* Experience Section */}
95 |
99 |
100 | Work Experience
101 |
102 |
103 | {data.workExperience.map((exp) => (
104 |
105 |
106 |
107 |
108 | {exp.role} @
109 |
114 | {exp.company}
115 |
116 |
117 |
118 | {exp.startDate} - {exp.endDate}
119 |
120 |
121 |
Key Responsibilities:
122 |
123 | {exp.keyResponsibilities.map((resp) => (
124 | - {resp}
125 | ))}
126 |
127 |
128 |
129 | ))}
130 |
131 |
132 |
133 | {/* Projects Section */}
134 |
138 | My Projects
139 |
140 | {data.projects.map((project) => (
141 |
142 |
143 |
150 |
151 |
152 |
153 |
154 | {project.title}
155 |
156 | {project.technologies.map((tech) => (
157 |
158 | {tech}
159 |
160 | ))}
161 |
162 |
163 |
164 | {project.description}
165 |
166 |
167 |
168 |
173 |
177 |
178 |
183 |
187 |
188 |
189 |
190 |
191 |
192 | ))}
193 |
194 |
195 |
196 | {/* Education Section */}
197 |
201 | Education
202 |
203 | {data.education.map((ed) => (
204 |
205 |
206 |
207 |
{ed.degree}
208 |
{ed.institution}
209 |
210 | {ed.startDate} - {ed.endDate}
211 |
212 |
{ed.description}
213 |
214 | ))}
215 |
216 |
217 |
218 | {/* Testimonials Section */}
219 |
223 | Testimonials
224 |
225 |
226 | {data.testimonials.map((t) => (
227 |
228 |
229 | “{t.feedback}.”
230 |
231 |
232 |
233 |
239 |
240 |
241 |
{t.name}
242 |
243 | {t.title} @ {t.company}
244 |
245 |
246 |
247 |
248 | ))}
249 |
250 |
251 |
252 | {/* Blogs Section */}
253 |
257 | Blogs
258 |
259 |
260 | {posts.map((post) => (
261 |
262 |
263 | {post.title}
264 |
265 |
{post.description}
266 |
267 | Published at: {post.publishDate}
268 |
269 |
270 | ))}
271 |
272 |
273 |
274 | );
275 | }
276 |
--------------------------------------------------------------------------------
/src/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AvatarPrimitive from "@radix-ui/react-avatar"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 | ))
21 | Avatar.displayName = AvatarPrimitive.Root.displayName
22 |
23 | const AvatarImage = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
34 |
35 | const AvatarFallback = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49 |
50 | export { Avatar, AvatarImage, AvatarFallback }
51 |
--------------------------------------------------------------------------------
/src/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | }
24 | )
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | )
34 | }
35 |
36 | export { Badge, badgeVariants }
37 |
--------------------------------------------------------------------------------
/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 | destructive:
15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 | outline:
17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18 | secondary:
19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 | ghost: "hover:bg-accent hover:text-accent-foreground",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-6 py-2",
25 | sm: "h-8 rounded-md px-4 text-xs",
26 | lg: "h-10 rounded-md px-8",
27 | icon: "h-9 w-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | );
36 |
37 | export interface ButtonProps
38 | extends React.ButtonHTMLAttributes,
39 | VariantProps {
40 | asChild?: boolean;
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({ className, variant, size, asChild = false, ...props }, ref) => {
45 | const Comp = asChild ? Slot : "button";
46 | return (
47 |
52 | );
53 | }
54 | );
55 | Button.displayName = "Button";
56 |
57 | export { Button, buttonVariants };
58 |
--------------------------------------------------------------------------------
/src/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Card = React.forwardRef<
6 | HTMLDivElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
17 | ))
18 | Card.displayName = "Card"
19 |
20 | const CardHeader = React.forwardRef<
21 | HTMLDivElement,
22 | React.HTMLAttributes
23 | >(({ className, ...props }, ref) => (
24 |
29 | ))
30 | CardHeader.displayName = "CardHeader"
31 |
32 | const CardTitle = React.forwardRef<
33 | HTMLParagraphElement,
34 | React.HTMLAttributes
35 | >(({ className, ...props }, ref) => (
36 |
44 | ))
45 | CardTitle.displayName = "CardTitle"
46 |
47 | const CardDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ))
57 | CardDescription.displayName = "CardDescription"
58 |
59 | const CardContent = React.forwardRef<
60 | HTMLDivElement,
61 | React.HTMLAttributes
62 | >(({ className, ...props }, ref) => (
63 |
64 | ))
65 | CardContent.displayName = "CardContent"
66 |
67 | const CardFooter = React.forwardRef<
68 | HTMLDivElement,
69 | React.HTMLAttributes
70 | >(({ className, ...props }, ref) => (
71 |
76 | ))
77 | CardFooter.displayName = "CardFooter"
78 |
79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
80 |
--------------------------------------------------------------------------------
/src/components/ui/footer.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Link from "next/dist/client/link";
3 |
4 | export default function Footer() {
5 | return (
6 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/ui/navbar.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { Sheet, SheetTrigger, SheetContent } from "@/components/ui/sheet";
3 | import { Button } from "@/components/ui/button";
4 | import { JSX, SVGProps } from "react";
5 | import { getJSONData } from "@/lib/serverUtils";
6 | import Image from "next/image";
7 | import ThemeToggler from "./themeToggler";
8 |
9 | export default async function Navbar() {
10 | const data = await getJSONData();
11 |
12 | return (
13 |
14 |
15 |
16 |
22 |
23 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 | {data.visual.navbar.links.map((item) => (
46 |
51 | {item.label}
52 |
53 | ))}
54 |
55 |
56 |
57 |
58 |
59 | );
60 | }
61 |
62 | function MenuIcon(props: JSX.IntrinsicAttributes & SVGProps) {
63 | return (
64 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/src/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SheetPrimitive from "@radix-ui/react-dialog"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 | import { X } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const Sheet = SheetPrimitive.Root
11 |
12 | const SheetTrigger = SheetPrimitive.Trigger
13 |
14 | const SheetClose = SheetPrimitive.Close
15 |
16 | const SheetPortal = SheetPrimitive.Portal
17 |
18 | const SheetOverlay = React.forwardRef<
19 | React.ElementRef,
20 | React.ComponentPropsWithoutRef
21 | >(({ className, ...props }, ref) => (
22 |
30 | ))
31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32 |
33 | const sheetVariants = cva(
34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
35 | {
36 | variants: {
37 | side: {
38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39 | bottom:
40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
42 | right:
43 | "inset-y-0 right-0 h-full w-3/4 data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
44 | },
45 | },
46 | defaultVariants: {
47 | side: "right",
48 | },
49 | }
50 | )
51 |
52 | interface SheetContentProps
53 | extends React.ComponentPropsWithoutRef,
54 | VariantProps {}
55 |
56 | const SheetContent = React.forwardRef<
57 | React.ElementRef,
58 | SheetContentProps
59 | >(({ side = "right", className, children, ...props }, ref) => (
60 |
61 |
62 |
67 | {children}
68 |
69 |
70 | Close
71 |
72 |
73 |
74 | ))
75 | SheetContent.displayName = SheetPrimitive.Content.displayName
76 |
77 | const SheetHeader = ({
78 | className,
79 | ...props
80 | }: React.HTMLAttributes) => (
81 |
88 | )
89 | SheetHeader.displayName = "SheetHeader"
90 |
91 | const SheetFooter = ({
92 | className,
93 | ...props
94 | }: React.HTMLAttributes) => (
95 |
102 | )
103 | SheetFooter.displayName = "SheetFooter"
104 |
105 | const SheetTitle = React.forwardRef<
106 | React.ElementRef,
107 | React.ComponentPropsWithoutRef
108 | >(({ className, ...props }, ref) => (
109 |
114 | ))
115 | SheetTitle.displayName = SheetPrimitive.Title.displayName
116 |
117 | const SheetDescription = React.forwardRef<
118 | React.ElementRef,
119 | React.ComponentPropsWithoutRef
120 | >(({ className, ...props }, ref) => (
121 |
126 | ))
127 | SheetDescription.displayName = SheetPrimitive.Description.displayName
128 |
129 | export {
130 | Sheet,
131 | SheetPortal,
132 | SheetOverlay,
133 | SheetTrigger,
134 | SheetClose,
135 | SheetContent,
136 | SheetHeader,
137 | SheetFooter,
138 | SheetTitle,
139 | SheetDescription,
140 | }
141 |
--------------------------------------------------------------------------------
/src/components/ui/themeToggler.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useEffect, useState } from "react";
4 | import { SunIcon, MoonIcon } from "@radix-ui/react-icons";
5 |
6 | export default function ThemeToggler() {
7 | const [globalTheme, setGlobalTheme] = useState('');
8 |
9 | useEffect(() => {
10 | setGlobalTheme(() => localStorage.getItem('globalTheme') || 'light');
11 | }, []);
12 |
13 | const toggleDarkTheme = () => {
14 | const newTheme = globalTheme === 'dark' ? 'light' : 'dark';
15 | localStorage.setItem('globalTheme', newTheme);
16 | setGlobalTheme(newTheme);
17 | };
18 |
19 | useEffect(() => {
20 | if (!globalTheme) {
21 | return;
22 | }
23 | if (globalTheme === 'light') {
24 | document?.body?.classList?.remove('dark');
25 | } else {
26 | document?.body?.classList?.add('dark');
27 | }
28 | }, [globalTheme]);
29 |
30 | return (
31 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/lib/serverUtils.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import path from "path";
4 | import fs from "fs";
5 | import { Data } from "@/types/data";
6 | import { BlogMetadata } from "@/types/blog";
7 |
8 | function isBlogHeaderData(data: any): data is BlogMetadata {
9 | return (
10 | typeof data.title === "string" &&
11 | typeof data.description === "string" &&
12 | typeof data.isPublished === "boolean" &&
13 | typeof data.slug === "string" &&
14 | typeof data.publishDate === "string" // or Date if you use Date
15 | );
16 | }
17 |
18 | export async function getJSONData(): Promise {
19 | const filePath = path.join(process.cwd(), "public", "data.json");
20 | const file = fs.readFileSync(filePath, "utf-8");
21 |
22 | return JSON.parse(file);
23 | }
24 |
25 | export async function getBlogPosts(): Promise {
26 | const contentDirPath = path.join(process.cwd(), "src/app/blogs");
27 | const postDirs = fs
28 | .readdirSync(contentDirPath, { withFileTypes: true })
29 | .filter((dir) => dir.isDirectory())
30 | .map((dir) => dir.name);
31 |
32 | const blogs = await Promise.all(
33 | postDirs.map(async (postDir) => {
34 | console.log(`@/app/blogs/${postDir}/page.tsx`);
35 | const { metadata } = await import(`@/app/blogs/${postDir}/page.mdx`);
36 |
37 | if (isBlogHeaderData(metadata)) {
38 | return metadata;
39 | }
40 |
41 | throw new Error("Blog data missing!");
42 | })
43 | );
44 |
45 | return blogs;
46 | }
47 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/src/mdx-components.tsx:
--------------------------------------------------------------------------------
1 | import type { MDXComponents } from "mdx/types";
2 |
3 | export function useMDXComponents(components: MDXComponents): MDXComponents {
4 | return {
5 | ...components,
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/types/blog.ts:
--------------------------------------------------------------------------------
1 | export interface BlogMetadata {
2 | title: string;
3 | description: string;
4 | isPublished: boolean;
5 | slug: string;
6 | publishDate: string;
7 | }
8 |
--------------------------------------------------------------------------------
/src/types/data.ts:
--------------------------------------------------------------------------------
1 | export interface NavbarLink {
2 | label: string;
3 | path: string;
4 | }
5 |
6 | export interface Navbar {
7 | links: NavbarLink[];
8 | }
9 |
10 | export interface HomeSections {
11 | banner: boolean;
12 | experience: boolean;
13 | project: boolean;
14 | education: boolean;
15 | testimonial: boolean;
16 | }
17 |
18 | export interface Home {
19 | sections: HomeSections;
20 | }
21 |
22 | type Visual = {
23 | navbar: Navbar;
24 | home: Home;
25 | };
26 |
27 | export interface PersonalInfo {
28 | name: string;
29 | title: string;
30 | location: string;
31 | bio: string;
32 | }
33 |
34 | export interface ContactInfo {
35 | email: string;
36 | phone: string;
37 | linkedin: string;
38 | twitter: string;
39 | github: string;
40 | website: string;
41 | }
42 |
43 | export interface Skills {
44 | languages: string[];
45 | frameworks: string[];
46 | databases: string[];
47 | tools: string[];
48 | }
49 |
50 | export interface Project {
51 | title: string;
52 | description: string;
53 | technologies: string[];
54 | live_url: string;
55 | code_repo_url: string;
56 | cover: string;
57 | }
58 |
59 | export interface WorkExperience {
60 | id: string;
61 | company: string;
62 | companyWebsite: string;
63 | role: string;
64 | startDate: string;
65 | endDate: string;
66 | technologies: string[];
67 | keyResponsibilities: string[];
68 | }
69 |
70 | export interface Education {
71 | id: string;
72 | institution: string;
73 | degree: string;
74 | description: string;
75 | startDate: string;
76 | endDate: string;
77 | }
78 |
79 | export interface Testimonial {
80 | id: string;
81 | name: string;
82 | avatar: string;
83 | title: string;
84 | company: string;
85 | feedback: string;
86 | }
87 |
88 | export interface Certification {
89 | title: string;
90 | institution: string;
91 | date: string;
92 | }
93 |
94 | export interface Data {
95 | personalInfo: PersonalInfo;
96 | contactInfo: ContactInfo;
97 | skills: Skills;
98 | projects: Project[];
99 | workExperience: WorkExperience[];
100 | education: Education[];
101 | certifications: Certification[];
102 | hobbies: string[];
103 | testimonials: Testimonial[];
104 | visual: Visual;
105 | }
106 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss"
2 |
3 | const config = {
4 | darkMode: ["class"],
5 | content: [
6 | './pages/**/*.{ts,tsx}',
7 | './components/**/*.{ts,tsx}',
8 | './app/**/*.{ts,tsx}',
9 | './src/**/*.{ts,tsx}',
10 | ],
11 | prefix: "",
12 | theme: {
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: "var(--radius)",
58 | md: "calc(var(--radius) - 2px)",
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | keyframes: {
62 | "accordion-down": {
63 | from: { height: "0" },
64 | to: { height: "var(--radix-accordion-content-height)" },
65 | },
66 | "accordion-up": {
67 | from: { height: "var(--radix-accordion-content-height)" },
68 | to: { height: "0" },
69 | },
70 | },
71 | animation: {
72 | "accordion-down": "accordion-down 0.2s ease-out",
73 | "accordion-up": "accordion-up 0.2s ease-out",
74 | },
75 | },
76 | },
77 | plugins: [require("tailwindcss-animate")],
78 | } satisfies Config
79 |
80 | export default config
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------