Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions fullstack/blog/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions fullstack/blog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [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.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

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/app/building-your-application/deploying) for more details.
14 changes: 14 additions & 0 deletions fullstack/blog/app/api/posts/[id]/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { posts } from "../../../data/posts";
import { NextResponse } from "next/server";

export async function GET(request, { params }) {
const { id } = await params;
const post = posts.find((p) => p.id === Number(id));
// console.log("post: ", post);

if (!post) {
return NextResponse.json({ error: "Post not found" }, { status: 404 });
}

return NextResponse.json(post);
}
6 changes: 6 additions & 0 deletions fullstack/blog/app/api/posts/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { posts } from "../../data/posts";
import { NextResponse } from "next/server";

export async function GET() {
return NextResponse.json(posts);
}
34 changes: 34 additions & 0 deletions fullstack/blog/app/data/posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const posts = [
{
id: 1,
title: "Getting Started with Next.js",
excerpt: "Learn the basics of Next.js and how to create your first app",
content:
"Next.js is a React framework that enables server-side rendering and generating static websites...",
date: "2025-04-15",
},
{
id: 2,
title: "Styling in Next.js",
excerpt: "Different ways to style your Next.js application",
content:
"There are multiple ways to style your Next.js application including CSS modules, Tailwind CSS...",
date: "2025-04-16",
},
{
id: 3,
title: "Routing in Next.js",
excerpt: "How to handle dynamic routes and navigation",
content:
"Next.js provides a file-based routing system and support for dynamic routes using brackets [id]...",
date: "2025-04-17",
},
{
id: 4,
title: "API Routes in Next.js",
excerpt: "Creating backend endpoints with Next.js API routes",
content:
"Next.js allows you to build API endpoints inside your project under the pages/api folder, useful for serverless functions...",
date: "2025-04-18",
},
];
Binary file added fullstack/blog/app/favicon.ico
Binary file not shown.
28 changes: 28 additions & 0 deletions fullstack/blog/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@import "tailwindcss";

:root {
--background: transparent;
--color-primary: #333333;
--color-secondary: #757575;

--font-sans: "Proxima Nova Light", "Helvetica Neue", Helvetica, Arial, sans-serif;

}

body {
background: var(--background);
color: var(--foreground);
font-family: var(--font-sans);
}

@layer utilities {
.text-primary {
color: var(--color-primary);
}

.text-secondary {
color: var(--color-secondary);
}


}
34 changes: 34 additions & 0 deletions fullstack/blog/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.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: "My Blog | Fullstack Next.js",
description: "A simple blog from codebility assessment",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
55 changes: 55 additions & 0 deletions fullstack/blog/app/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import axios from "axios";
import Link from "next/link";

const HomePage = async () => {
let posts = [];
try {
const res = await axios.get("http://localhost:3000/api/posts");
posts = res.data;
// console.log("response: ", res);
// console.log("posts: ", posts);
} catch (error) {
console.error("Error fetching posts: ", error);
}
return (
<main className="flex flex-col items-center my-20">
<h1 className="mb-10 text-primary text-6xl font-bold">Blogs</h1>

{posts.length === 0 ? (
<p className="text-secondary text-3xl">No posts available</p>
) : (
<div className="flex flex-col gap-10">
{posts.map((post) => (
<Link
key={post.id}
href={`/posts/${post.id}`}
className="group flex flex-col border px-12 py-10 rounded-2xl border-gray-400 hover:scale-105 transition-all duration-300 hover:shadow-lg hover:shadow-gray-400"
>
<h1 className="text-primary text-4xl font-semibold ">
{post.title}
</h1>
<p className="mt-4 text-secondary text-xl">{post.excerpt}</p>
<p className="mt-1 text-sm text-secondary ">
Last updated{" "}
{new Date(post.date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>

<div
href={`/posts/${post.id}`}
className="mt-3 ml-auto text-primary font-medium opacity-70 group-hover:opacity-100"
>
Read More &gt;
</div>
</Link>
))}
</div>
)}
</main>
);
};

export default HomePage;
44 changes: 44 additions & 0 deletions fullstack/blog/app/posts/[id]/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import axios from "axios";
import Link from "next/link";

const PostPage = async ({ params }) => {
const { id } = await params;
let post = null;

try {
const res = await axios.get(`http://localhost:3000/api/posts/${id}`);
post = res.data;
// console.log(post);
} catch (error) {
console.error("Error fetching post: ", error);
}

return (
<main className="flex flex-col items-center max-w-2xl mx-3 md:mx-auto my-20 gap-5">
<div>
<h1 className="mb-5 text-primary text-4xl md:text-5xl font-semibold ">
{post.title}
</h1>
<p className="mt-4 text-secondary text-xl">{post.excerpt}</p>
<p className="text-sm text-secondary ">
{new Date(post.date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
<p className="mt-6 text-primary text-lg">{post.content}</p>
</div>
<div className="px-6 py-3 self-end border border-transparent hover:border-gray-500 hover:scale-105 rounded-3xl">
<Link
href="/"
className="text-primary font-medium opacity-80 hover:opacity-100 hover:scale-105"
>
Back to Blogs
</Link>
</div>
</main>
);
};

export default PostPage;
18 changes: 18 additions & 0 deletions fullstack/blog/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
7 changes: 7 additions & 0 deletions fullstack/blog/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
};

export default nextConfig;
Loading