diff --git a/backend/src/routes/courses.ts b/backend/src/routes/courses.ts index 2139299..53800fb 100644 --- a/backend/src/routes/courses.ts +++ b/backend/src/routes/courses.ts @@ -16,6 +16,18 @@ router.get("/", async (req: Request, res: Response) => { } }); +// GET /courses/popular +router.get("/popular", async (req: Request, res: Response) => { + try { + const courses = await getAllCourses(ctx); + const popularCourses = courses.slice(0, 3); + res.status(200).json(popularCourses); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Failed to fetch popular courses." }); + } +}); + // GET /courses/:slug router.get("/:slug", async (req: Request<{ slug: string }>, res: Response) => { try { diff --git a/frontend/src/app/courses/CourseCard.tsx b/frontend/src/app/courses/CourseCard.tsx new file mode 100644 index 0000000..a6860cb --- /dev/null +++ b/frontend/src/app/courses/CourseCard.tsx @@ -0,0 +1,44 @@ +import moment from "moment"; +import Link from "next/link"; +import { type Course } from "@/types"; + +export default function CourseCard({ course }: { course: Course }) { + return ( + +
+
+ {course.difficulty} +
+ + {moment.duration(course.duration).humanize()} + +
+
+ {course.title} +
+

+ {course.title} +

+

+ {course.shortDescription} +

+ +
+ + {course?.instructor?.name} + +
+ + ); +} diff --git a/frontend/src/app/courses/[slug]/page.tsx b/frontend/src/app/courses/[slug]/page.tsx index ef6a7ad..cbf2ba8 100644 --- a/frontend/src/app/courses/[slug]/page.tsx +++ b/frontend/src/app/courses/[slug]/page.tsx @@ -80,87 +80,124 @@ export default async function Courses({ }; return ( -
-
+
+ {/* Header Section */} +
-

+

{course.title}

- -

- {course.shortDescription} -

+
- +
+
+ {course.instructor?.name + + {course.instructor.name} + +
+
+ + {course.difficulty} + + + {moment.duration(course.duration).humanize()} + +
-
- - {course.difficulty} - - - {moment.duration(course.duration).humanize()} - +
+

+ {course.shortDescription} +

+
-
-
); diff --git a/frontend/src/app/courses/page.tsx b/frontend/src/app/courses/page.tsx index b41926e..4b8a8fe 100644 --- a/frontend/src/app/courses/page.tsx +++ b/frontend/src/app/courses/page.tsx @@ -1,10 +1,6 @@ -import Link from "next/link"; -import { type Prisma } from "@prisma/client"; import { computeCourseDuration } from "@/lib/utils"; - -type Course = Prisma.CourseGetPayload<{ - include: { instructor: true }; -}> & { duration: number }; +import { type Course } from "@/types"; +import CourseCard from "./CourseCard"; export const dynamic = "force-dynamic"; @@ -19,42 +15,7 @@ export default async function Courses() { return (
{courses.map((course: Course) => ( - -
-
- {course.difficulty} -
- - {course.duration} - -
-
- {course.title} -
-

- {course.title} -

-

- {course.shortDescription} -

- -
- - {course?.instructor?.name} - -
- + ))}
); diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index d05f8ee..2e02573 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -22,8 +22,9 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "BrightPath", + description: + "BrightPath is a custom platform for online courses covering niche tech topics not currently addressed elsewhere.", }; export default async function RootLayout({ diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 0419e7f..1e8fe11 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,102 +1,100 @@ -"use client"; -import Image from "next/image"; +import { type Course } from "@/types"; +import CourseCard from "./courses/CourseCard"; -export default function Home() { - return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - src/app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
+const getPopularCourses = async () => { + const res = await fetch(process.env.BACKEND_API_URL + "/courses/popular"); + + if (!res.ok) { + return []; + } + + return await res.json(); +}; + +export const dynamic = "force-dynamic"; +export default async function Home() { + const popularCourses = await getPopularCourses(); -
- - Vercel logomark - Deploy now - - - Read our docs - + return ( + <> + {/* Hero Section */} +
+
+

+ Welcome to BrightPath! +

+

+ Ready to take your dev journey deeper? Learn by doing, + with hands-on project-based exercises, and stand out + from the crowd with specialized knowledge and skills. +

+

+ The BrightPath platform and courses + were created by a multidisciplinary team of software + development professionals in order to give back to our + community as we learn in public. +

-
- -
+
+ + + {/* Courses Section */} +
+

BrightPath Courses

+
+ {popularCourses.map((course: Course) => ( + + ))} +
+
+ + {/* Testimonials */} +
+

Testimonials

+
+ {[ + "Zuaida really kept our team on track.", + "Joseph does a great job demonstrating tech tools.", + "BrightPath is a great place to grow as a developer.", + ].map((testimonial, idx) => ( +
+ {testimonial} +
+ ))} +
+
+ + {/* Certification */} +
+

Certified learning.

+
+
+

John Doe

+

+ Has successfully completed all coursework and + assignments for the course +

+

+ Bring Your App to Life with Tailwind Motion +

+
+
+

+ Our verified certificates highlight your + specialization and skills. +

+
+
+
+ ); } diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts new file mode 100644 index 0000000..f3e824c --- /dev/null +++ b/frontend/src/types.d.ts @@ -0,0 +1,5 @@ +import { Prisma } from "@prisma/client"; + +export type Course = Prisma.CourseGetPayload<{ + include: { instructor: true }; +}> & { duration: number }; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 72771c2..1a14510 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -23,6 +23,12 @@ "auth": ["./src/auth"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "./src/types.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], "exclude": ["node_modules", "tests"] }