Skip to content
16 changes: 6 additions & 10 deletions app/(app)/feature-flag-example/_client.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
"use client";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole folder should have been left where it was an untouched. It is just to use as an example to show how to do feature flags.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was meant to be kept as an example. I don't think I was clear enough in the issue so that's my fault. But it is meant to be in app/(app)/courses so when a user visits /courses they can find this page.

import { type Session } from "next-auth";
import { notFound } from "next/navigation";
import { isFlagEnabled, FEATURE_FLAGS } from "@/utils/flags";
import CoursesLanding from "@/components/Course";

const Content = ({ session }: { session: Session | null }) => {
const flagEnabled = isFlagEnabled(FEATURE_FLAGS.FEATURE_FLAG_TEST);
const flagEnabled = isFlagEnabled(FEATURE_FLAGS.COURSES_LANDING); // Adjust to the correct flag

if (!flagEnabled) {
notFound();
notFound(); // Show 404 page if the feature flag is not enabled
}

return (
<div className="mx-auto max-w-2xl">
<h1 className="text-lg">
This page is behind a feature flag. It will work in development or when
the flag is turned on.
</h1>
<p className="mt-8 text-sm">
{session ? "User is logged in" : "User is not logged in"}
</p>
<div className="mx-auto max-w-6xl">
<CoursesLanding session={session} />
</div>
);
};
Expand Down
6 changes: 3 additions & 3 deletions app/(app)/feature-flag-example/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Content from "./_client";
import { getServerAuthSession } from "@/server/auth";

export const metadata = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again this should be in the /courses path

title: "This is a feature flag example",
title: "Courses Landing Page",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistency between page title and file path

The metadata title has been updated to "Courses Landing Page", which aligns with the PR objectives. However, the file is still located in the feature-flag-example directory. This inconsistency could lead to confusion.

Consider moving this file to a more appropriate location, such as app/(app)/courses/page.tsx, to match its new purpose. If you decide to keep it in the current location, please provide a comment explaining why it remains in the feature flag example directory.

};

export default async function Page() {
// Example of grabbing session in case it is needed
export default async function CoursesPage() {
// Get session if needed for authentication purposes
const session = await getServerAuthSession();

return <Content session={session} />;
Expand Down
42 changes: 42 additions & 0 deletions components/Course/CourseCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

import React from "react";
import { Course } from "./type";

interface CourseCardProps {
course: Course;
onStartCourse: (courseId: number) => void; // Change to number to match your mock data
}

const CourseCard: React.FC<CourseCardProps> = ({ course, onStartCourse }) => {
return (
<div
className={`rounded-lg p-6 shadow-md ${course.featured ? "bg-orange-100 dark:bg-orange-900" : "bg-gray-100 dark:bg-gray-800"}`}
>
{/* Course Title */}
<h2 className="mb-4 text-2xl font-bold">{course.title}</h2>

{/* Course Description */}
<p className="mb-4 text-lg text-gray-700 dark:text-gray-300">
{course.description}
</p>

{/* Course Progress */}
<div className="flex items-center gap-4">
<p className="text-lg">Progress:</p>
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-pink-500 text-lg font-bold">
{course.progress}%
</div>
</div>

{/* Start Course Button */}
<button
className="mt-4 rounded bg-orange-500 px-4 py-2 text-white hover:bg-orange-600"
onClick={() => onStartCourse(Number(course.id))} // Call the passed function with course ID
>
Start Course
</button>
</div>
);
};

export default CourseCard;
32 changes: 32 additions & 0 deletions components/Course/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

import React from "react";
import { mockCourses, userProgress } from "./mock";
import CourseCard from "./CourseCard";
import { type Session } from "next-auth";

const CoursesLanding = ({ session }: { session: Session | null }) => {
const handleStartCourse = (courseId: number) => {
console.log(`Starting course with ID: ${courseId}`);
// Add logic here to navigate to the course content page or start the course
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement navigation logic for course start.

The handleStartCourse function is currently a placeholder. To ensure complete functionality:

  1. Implement the navigation logic to the course content page.
  2. Consider using Next.js's useRouter hook for navigation.
  3. Remove the console.log statement once the navigation is implemented.

Would you like assistance in implementing this navigation logic or creating a GitHub issue to track this task?


return (
<div className="flex min-h-screen flex-col gap-8 px-4 py-8 dark:bg-gray-900 dark:text-white lg:px-16 lg:py-12">
{/* Page Title */}
<h1 className="mb-6 text-3xl font-bold lg:text-4xl">Courses</h1>

{/* Courses List */}
<div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
{mockCourses.map((course) => (
<CourseCard
key={course.id}
course={course}
onStartCourse={handleStartCourse}
/>
))}
</div>
</div>
);
};

export default CoursesLanding;
54 changes: 54 additions & 0 deletions components/Course/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Course } from "./type";

export const mockCourses: Course[] = [
{
id: "1",
title: "Introduction to Web Development",
description:
"Learn the basics of HTML, CSS, and JavaScript to build responsive websites.",
progress: 90,
featured: true,
},
{
id: "2",
title: "Advanced JavaScript Concepts",
description:
"Deep dive into JavaScript with advanced concepts and techniques.",
progress: 65,
featured: false,
},
{
id: "3",
title: "React for Beginners",
description: "Get started with React and build interactive UIs.",
progress: 30,
featured: false,
},
{
id: "4",
title: "Full-Stack Web Development",
description:
"Become a full-stack developer by learning both front-end and back-end technologies.",
progress: 45,
featured: true,
},
{
id: "5",
title: "Version Control with Git and GitHub",
description:
"Learn how to use Git for version control and GitHub for collaboration.",
progress: 80,
featured: false,
},
];

// Function to generate user progress from courses
export const generateUserProgress = (courses: Course[]) => ({
coursesProgress: courses.map((course) => ({
courseId: course.id,
progress: course.progress,
featured: course.featured,
})),
});

export const userProgress = generateUserProgress(mockCourses);
7 changes: 7 additions & 0 deletions components/Course/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Course {
id: string;
title: string;
description: string;
progress: number;
featured: boolean;
}
6 changes: 3 additions & 3 deletions utils/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { posthog } from "posthog-js";

export const FEATURE_FLAGS = {
FEATURE_FLAG_TEST: "feature-flag-test",
// Add more feature flags as needed
COURSES_LANDING: "courses-landing",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is perfect! Thanks

} as const;

export type FeatureFlagName =
Expand All @@ -17,11 +17,11 @@ export function isDevEnvironment() {

export const isFlagEnabled = (
featureFlag: FeatureFlagName,
disableDevCheck = false, // Disable dev check to force feature flag to be checked always
disableDevCheck = false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add back in the comment here as it's a note to developer

) => {
if (!disableDevCheck && isDevEnvironment()) {
console.log("Feature flag check skipped in development environment");
return true;
return true; // Always true in dev environments unless you want to test differently
}
return posthog.isFeatureEnabled(featureFlag);
};