├── README.md ├── nextjs-frontend ├── .eslintrc.json ├── .gitignore ├── README.md ├── app │ ├── about │ │ └── page.js │ ├── blog │ │ └── page.js │ ├── blogpost │ │ └── [slug] │ │ │ └── page.js │ ├── favicon.ico │ ├── fonts │ │ ├── GeistMonoVF.woff │ │ └── GeistVF.woff │ ├── globals.css │ ├── layout.js │ └── page.js ├── components.json ├── components │ ├── Footer.js │ ├── MarkdownHTML.js │ ├── Navbar.js │ └── ui │ │ ├── button.jsx │ │ └── sheet.jsx ├── jsconfig.json ├── lib │ └── utils.js ├── next.config.mjs ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── a.png │ ├── blogimg.jpg │ ├── images │ │ ├── coding.jpeg │ │ └── cpp.png │ ├── logo.jpg │ ├── next.svg │ ├── typescript.webp │ └── vercel.svg └── tailwind.config.js └── strapi-backend ├── .env.example ├── .gitignore ├── README.md ├── config ├── admin.js ├── api.js ├── database.js ├── middlewares.js ├── plugins.js └── server.js ├── data ├── data.json └── uploads │ ├── a-bug-is-becoming-a-meme-on-the-internet.jpg │ ├── beautiful-picture.jpg │ ├── coffee-art.jpg │ ├── coffee-beans.jpg │ ├── coffee-shadow.jpg │ ├── daviddoe@strapi.io.jpg │ ├── default-image.png │ ├── favicon.png │ ├── sarahbaker@strapi.io.jpg │ ├── the-internet-s-own-boy.jpg │ ├── this-shrimp-is-awesome.jpg │ ├── we-love-pizza.jpg │ └── what-s-inside-a-black-hole.jpg ├── database └── migrations │ └── .gitkeep ├── favicon.png ├── jsconfig.json ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── public ├── robots.txt └── uploads │ └── .gitkeep ├── scripts └── seed.js ├── src ├── admin │ ├── app.example.js │ └── vite.config.example.js ├── api │ ├── .gitkeep │ ├── about │ │ ├── content-types │ │ │ └── about │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── about.js │ │ ├── routes │ │ │ └── about.js │ │ └── services │ │ │ └── about.js │ ├── article │ │ ├── content-types │ │ │ └── article │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── article.js │ │ ├── routes │ │ │ └── article.js │ │ └── services │ │ │ └── article.js │ ├── author │ │ ├── content-types │ │ │ └── author │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── author.js │ │ ├── routes │ │ │ └── author.js │ │ └── services │ │ │ └── author.js │ ├── category │ │ ├── content-types │ │ │ └── category │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── category.js │ │ ├── routes │ │ │ └── category.js │ │ └── services │ │ │ └── category.js │ ├── global │ │ ├── content-types │ │ │ └── global │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── global.js │ │ ├── routes │ │ │ └── global.js │ │ └── services │ │ │ └── global.js │ ├── hometext │ │ ├── content-types │ │ │ └── hometext │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── hometext.js │ │ ├── routes │ │ │ └── hometext.js │ │ └── services │ │ │ └── hometext.js │ ├── response │ │ ├── content-types │ │ │ └── response │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── response.js │ │ ├── routes │ │ │ └── response.js │ │ └── services │ │ │ └── response.js │ └── site-text │ │ ├── content-types │ │ └── site-text │ │ │ └── schema.json │ │ ├── controllers │ │ └── site-text.js │ │ ├── routes │ │ └── site-text.js │ │ └── services │ │ └── site-text.js ├── components │ └── shared │ │ ├── image.json │ │ ├── media.json │ │ ├── quote.json │ │ ├── rich-text.json │ │ ├── seo.json │ │ └── slider.json ├── extensions │ └── .gitkeep └── index.js └── types └── generated ├── components.d.ts └── contentTypes.d.ts /README.md: -------------------------------------------------------------------------------- 1 | # Strapi 5 Tutorial 2 | 3 | This repository contains a tutorial project that demonstrates how to build a full-stack application using Next.js for the frontend and Strapi for the backend. 4 | 5 | ## Getting Started 6 | 7 | ### Prerequisites 8 | 9 | Make sure you have the following installed on your system: 10 | 11 | - Node.js (>=14.x) 12 | - npm (>=6.x) 13 | 14 | ### Project Structure 15 | 16 | - **nextjs-frontend/**: This directory contains the frontend code, which is built using Next.js. 17 | - **strapi-backend/**: This directory contains the backend code, which is built using Strapi, a headless CMS. 18 | 19 | ## Running the Project 20 | 21 | ### 1. Frontend (Next.js) 22 | 23 | To run the frontend application: 24 | 25 | 1. Navigate to the `nextjs-frontend` directory. 26 | ```bash 27 | cd nextjs-frontend 28 | ``` 29 | 30 | 2. Install the dependencies: 31 | ```bash 32 | npm install 33 | ``` 34 | 35 | 3. Start the development server: 36 | ```bash 37 | npm run dev 38 | ``` 39 | 40 | The frontend will be running at `http://localhost:3000`. 41 | 42 | ### 2. Backend (Strapi) 43 | 44 | To run the backend application: 45 | 46 | 1. Navigate to the `strapi-backend` directory. 47 | ```bash 48 | cd strapi-backend 49 | ``` 50 | 51 | 2. Install the dependencies: 52 | ```bash 53 | npm install 54 | ``` 55 | 56 | 3. Start the Strapi development server: 57 | ```bash 58 | npm run develop 59 | ``` 60 | 61 | The backend will be running at `http://localhost:1337`. If you have Razer Chroma installed, change the port in the Strapi configuration to avoid conflicts. 62 | -------------------------------------------------------------------------------- /nextjs-frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /nextjs-frontend/.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 | -------------------------------------------------------------------------------- /nextjs-frontend/README.md: -------------------------------------------------------------------------------- 1 | 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). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | 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. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /nextjs-frontend/app/about/page.js: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | export const metadata = { 4 | title: "About | Harry's Developer Portfolio", 5 | } 6 | 7 | export default function About() { 8 | return ( 9 |
10 |
11 |
12 |
13 |
14 |
15 | Profile 22 |
23 |
24 |
25 |

About Me

26 |

27 | Hello! I'm Harry, a passionate software developer and tech enthusiast. I created this blog to share my experiences, tips, and tutorials on various programming languages and technologies. I believe that learning should be a continuous journey, and I'm here to help others on their path to mastering the art of coding. 28 |

29 |

30 | Whether you're just starting out or looking to sharpen your skills, you'll find a variety of resources and insights here. Let's explore the world of programming together! 31 |

32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |

Harry's Journey as a Coder

41 |

42 | From curious beginner to seasoned developer, here's how Harry navigated the world of programming. 43 |

44 |
45 |
46 |
47 |
48 | Harry as a beginner 49 |
50 |
51 |

The Spark of Curiosity

52 |

53 | Harry's coding journey began in high school when he stumbled upon his first programming language—Python. What started as a simple curiosity quickly turned into a passion, as Harry spent countless hours experimenting with code, building small projects, and learning the fundamentals of software development. 54 |

55 |
56 |
57 | 58 |
59 |
60 | Harry learning new skills 61 |
62 |
63 |

Diving Deeper

64 |

65 | After mastering the basics, Harry's thirst for knowledge grew. He began exploring more complex topics such as data structures, algorithms, and web development. Enrolling in online courses and attending coding bootcamps, Harry quickly expanded his skill set, taking on freelance projects to apply his knowledge in real-world scenarios. 66 |

67 |
68 |
69 | 70 |
71 |
72 | Harry working on a big project 73 |
74 |
75 |

Taking on Challenges

76 |

77 | With several years of experience under his belt, Harry began tackling more significant challenges. From contributing to open-source projects to developing his own applications, Harry continued to push his limits, always looking for opportunities to learn and grow. His journey wasn't without its setbacks, but each obstacle was a stepping stone to becoming the skilled developer he is today. 78 |

79 |
80 |
81 | 82 |
83 |
84 | Harry mentoring others 85 |
86 |
87 |

Giving Back

88 |

89 | Today, Harry is not only a proficient coder but also a mentor to others. He regularly contributes to the programming community by writing tutorials, giving talks, and helping new coders find their footing in the world of software development. For Harry, coding is more than just a profession—it's a lifelong journey of learning and sharing knowledge. 90 |

91 |
92 |
93 |
94 |
95 |
96 | 97 |
98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /nextjs-frontend/app/blog/page.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | import { Button } from "@/components/ui/button" 4 | 5 | export const metadata = { 6 | title: "Blogs | Harry's Developer Portfolio", 7 | } 8 | // Fetch the data 9 | const data = await fetch("http://localhost:1337/api/articles?populate=* ", { cache: 'no-store' }) 10 | const response = await data.json() 11 | 12 | // console.log(response) 13 | 14 | const Blog = () => { 15 | return ( 16 |
17 |
18 |

Blogs

19 |
20 | {response.data && response.data.map(data => { 21 | return ( // Add return here 22 | 23 |
24 | {/* data post image */} 25 | {data.title} 26 | 27 | {/* data post content */} 28 |
29 | {/* data post title */} 30 |

{data.title}

31 | 32 | {/* data post description */} 33 |

{data.description.split(' ').length > 6 ? data.description.split(' ').slice(0, 11).join(' ') + '...' : data.description}

34 | 35 | {/* data post author and date */} 36 |
37 | By {data.author.name} | {new Date(data.publishedAt).toLocaleDateString('en-GB', { day: '2-digit', month: 'long', year: 'numeric' })} 38 |
39 | 40 | 41 | 42 |
43 |
44 | ) 45 | })} 46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | export default Blog; 53 | -------------------------------------------------------------------------------- /nextjs-frontend/app/blogpost/[slug]/page.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React, { useState, useEffect } from 'react'; 3 | import MarkdownHTML from '@/components/MarkdownHTML'; 4 | import Link from 'next/link'; 5 | import axios from 'axios'; 6 | 7 | 8 | export default function Page({ params }) { 9 | const { slug } = params; 10 | const [response, setBlog] = useState(); 11 | const [postResponse, setPost] = useState(); 12 | const [mediaData, setMediaData] = useState(null); 13 | 14 | // Fetch blog data 15 | const fetchData = async () => { 16 | const url = `http://localhost:1337/api/articles?sort[0]=title:asc&filters[slug][$eq]=${slug}&status=published&locale[0]=en&populate=*`; 17 | const data = await fetch(url, { cache: 'no-store' }); 18 | const response = await data.json(); 19 | setBlog(response); 20 | document.title = `${response.data[0].title} | Harry's Developer Portfolio`; 21 | }; 22 | 23 | // Fetch related posts 24 | const fetchPosts = async () => { 25 | const url = `http://localhost:1337/api/articles?populate=*`; 26 | const data2 = await fetch(url, { cache: 'no-store' }); 27 | const postResponse = await data2.json(); 28 | setPost(postResponse); 29 | }; 30 | 31 | 32 | useEffect(() => { 33 | fetchData(); 34 | fetchPosts(); 35 | fetchMedia(); 36 | }, []); 37 | 38 | 39 | return ( 40 |
41 |
42 | {/* Blog Title */} 43 |

44 | {response && response.data[0].title} 45 |

46 | 47 | {/* Author and Date */} 48 |
49 |
50 |

Written by {response && response.data[0].author.name}

51 |

Published on October 6, 2024

52 |
53 |
54 | 55 | {/* Blog Content */} 56 |
57 | {response && response.data[0].blocks.map((item) => { 58 | switch (item["__component"]) { 59 | case "shared.rich-text": 60 | return ; 61 | 62 | case "shared.quote": 63 | return ( 64 |
65 |

{item.body}

66 | — {item.title} 67 |
68 | ); 69 | 70 | 71 | case "shared.slider": 72 | return ( 73 |
74 |

Slider content goes here

75 |
76 | ); 77 | 78 | default: 79 | return null; 80 | } 81 | })} 82 |
83 | 84 | {/* Related Posts Section */} 85 |
86 |

Related Posts

87 |
88 | {postResponse && postResponse.data.map((data) => ( 89 | 90 |
91 |

{data.title}

92 |

93 | {data.description.split(' ').length > 6 94 | ? data.description.split(' ').slice(0, 11).join(' ') + '...' 95 | : data.description} 96 |

97 |
98 | 99 | ))} 100 |
101 |
102 |
103 |
104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /nextjs-frontend/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeWithHarry/Strap5-tutorial/62f70e19a0d5a1fd9a377dbcec88bd7ce01a3409/nextjs-frontend/app/favicon.ico -------------------------------------------------------------------------------- /nextjs-frontend/app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeWithHarry/Strap5-tutorial/62f70e19a0d5a1fd9a377dbcec88bd7ce01a3409/nextjs-frontend/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /nextjs-frontend/app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeWithHarry/Strap5-tutorial/62f70e19a0d5a1fd9a377dbcec88bd7ce01a3409/nextjs-frontend/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /nextjs-frontend/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | font-family: Arial, Helvetica, sans-serif; 7 | } 8 | 9 | @layer utilities { 10 | .text-balance { 11 | text-wrap: balance; 12 | } 13 | } 14 | 15 | @layer base { 16 | :root { 17 | --background: 0 0% 100%; 18 | --foreground: 240 10% 3.9%; 19 | --card: 0 0% 100%; 20 | --card-foreground: 240 10% 3.9%; 21 | --popover: 0 0% 100%; 22 | --popover-foreground: 240 10% 3.9%; 23 | --primary: 240 5.9% 10%; 24 | --primary-foreground: 0 0% 98%; 25 | --secondary: 240 4.8% 95.9%; 26 | --secondary-foreground: 240 5.9% 10%; 27 | --muted: 240 4.8% 95.9%; 28 | --muted-foreground: 240 3.8% 46.1%; 29 | --accent: 240 4.8% 95.9%; 30 | --accent-foreground: 240 5.9% 10%; 31 | --destructive: 0 84.2% 60.2%; 32 | --destructive-foreground: 0 0% 98%; 33 | --border: 240 5.9% 90%; 34 | --input: 240 5.9% 90%; 35 | --ring: 240 10% 3.9%; 36 | --chart-1: 12 76% 61%; 37 | --chart-2: 173 58% 39%; 38 | --chart-3: 197 37% 24%; 39 | --chart-4: 43 74% 66%; 40 | --chart-5: 27 87% 67%; 41 | --radius: 0.5rem; 42 | } 43 | .dark { 44 | --background: 240 10% 3.9%; 45 | --foreground: 0 0% 98%; 46 | --card: 240 10% 3.9%; 47 | --card-foreground: 0 0% 98%; 48 | --popover: 240 10% 3.9%; 49 | --popover-foreground: 0 0% 98%; 50 | --primary: 0 0% 98%; 51 | --primary-foreground: 240 5.9% 10%; 52 | --secondary: 240 3.7% 15.9%; 53 | --secondary-foreground: 0 0% 98%; 54 | --muted: 240 3.7% 15.9%; 55 | --muted-foreground: 240 5% 64.9%; 56 | --accent: 240 3.7% 15.9%; 57 | --accent-foreground: 0 0% 98%; 58 | --destructive: 0 62.8% 30.6%; 59 | --destructive-foreground: 0 0% 98%; 60 | --border: 240 3.7% 15.9%; 61 | --input: 240 3.7% 15.9%; 62 | --ring: 240 4.9% 83.9%; 63 | --chart-1: 220 70% 50%; 64 | --chart-2: 160 60% 45%; 65 | --chart-3: 30 80% 55%; 66 | --chart-4: 280 65% 60%; 67 | --chart-5: 340 75% 55%; 68 | } 69 | } 70 | 71 | @layer base { 72 | * { 73 | @apply border-border; 74 | } 75 | body { 76 | @apply bg-background text-foreground; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /nextjs-frontend/app/layout.js: -------------------------------------------------------------------------------- 1 | import localFont from "next/font/local"; 2 | import "./globals.css"; 3 | import Navbar from "@/components/Navbar"; 4 | import Footer from "@/components/Footer"; 5 | 6 | const geistSans = localFont({ 7 | src: "./fonts/GeistVF.woff", 8 | variable: "--font-geist-sans", 9 | weight: "100 900", 10 | }); 11 | const geistMono = localFont({ 12 | src: "./fonts/GeistMonoVF.woff", 13 | variable: "--font-geist-mono", 14 | weight: "100 900", 15 | }); 16 | 17 | export const metadata = { 18 | title: "Create Next App", 19 | description: "Generated by create next app", 20 | }; 21 | 22 | export default function RootLayout({ children }) { 23 | return ( 24 | 25 | 28 | 29 | {children} 30 |