Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
39a6990
Add organisation settings page
lavanyagarg112 May 24, 2025
2036f25
Fetch data from backend
lavanyagarg112 May 24, 2025
5ee944b
Call api endpoint to update settings
lavanyagarg112 May 24, 2025
df4f8fe
Save initial data and save only when needed
lavanyagarg112 May 24, 2025
bdecd79
Add page for organisation courses
lavanyagarg112 May 24, 2025
7d87f35
Add structure for courses
lavanyagarg112 May 24, 2025
a312ae5
Add guard for non authorised users
lavanyagarg112 May 24, 2025
2eb7dab
Add basic course pages
lavanyagarg112 May 24, 2025
1601454
Add module edit flag
lavanyagarg112 May 29, 2025
5b34bc2
Add isEdit flag based on role
lavanyagarg112 May 29, 2025
75594bf
Add api call to add new course
lavanyagarg112 May 29, 2025
d8b11c0
Add endpoint to fetch courses
lavanyagarg112 May 29, 2025
8f188f8
Add api call to delete course
lavanyagarg112 May 29, 2025
d00ec8e
Add editing of courses
lavanyagarg112 May 29, 2025
9659a20
Fetch modules from backend
lavanyagarg112 May 29, 2025
dbbc8b4
disable reload to auth in layoutClient
lavanyagarg112 May 29, 2025
58c18df
Add safeguard on dashboard page
lavanyagarg112 May 29, 2025
28698b9
fix issue of reloading pages to /auth everytime
lavanyagarg112 May 29, 2025
a37ad11
Clean code
lavanyagarg112 May 29, 2025
980ab59
Add placeholders for new module
lavanyagarg112 May 29, 2025
d5e6365
Add logic for file upload
lavanyagarg112 May 29, 2025
12e90fd
Add delete module
lavanyagarg112 May 29, 2025
2bd260e
Add interface to fetch module data and display it
lavanyagarg112 May 29, 2025
bb183ca
add in progress message on dashboard
lavanyagarg112 May 29, 2025
c218cf0
fix delete error
lavanyagarg112 May 29, 2025
6dfe4ec
Add functionality to edit a module
lavanyagarg112 May 29, 2025
420f68a
make wording less ambiguous
lavanyagarg112 May 29, 2025
191f2d8
clean code
lavanyagarg112 May 29, 2025
febaab5
Add placeholder for users list
lavanyagarg112 May 29, 2025
e500c6b
Add interface to view users in an organisation
lavanyagarg112 May 29, 2025
044418d
add alert message for not implemented
lavanyagarg112 May 29, 2025
2131714
Make navbar more visible
lavanyagarg112 May 29, 2025
377571b
Remove users from organisation
lavanyagarg112 May 29, 2025
6cccbb0
Add interface to generate and join using invite code
lavanyagarg112 May 29, 2025
2128cfa
Merge pull request #22 from lavanyagarg112/lavanya/organisation-dashb…
lavanyagarg112 May 29, 2025
ac3ee0b
Remove breadcrumb for now
lavanyagarg112 May 30, 2025
6f29e0d
Merge pull request #23 from lavanyagarg112/lavanya/organisation-dashb…
lavanyagarg112 May 30, 2025
354e003
Employee onboards using invite code only
lavanyagarg112 Jun 8, 2025
9eefeb6
Merge pull request #24 from lavanyagarg112/lavanya/fix-invite-code
lavanyagarg112 Jun 8, 2025
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
29 changes: 29 additions & 0 deletions app/AuthClientProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import { useEffect } from "react";
import { useRouter, usePathname } from "next/navigation";
import { useAuth } from "@/context/AuthContext";

export default function AuthClientProvider({
children,
}: {
children: React.ReactNode;
}) {
const router = useRouter();
const pathname = usePathname();
const { user, loading } = useAuth();

useEffect(() => {
if (loading) return;
if (pathname === "/" || pathname === "/auth") return;
if (!user.isLoggedIn) {
router.replace("/auth");
return;
}
if (!user.hasCompletedOnboarding && pathname !== "/onboarding") {
router.replace("/onboarding");
}
}, [loading, pathname, user, router]);

return <>{children}</>;
}
10 changes: 10 additions & 0 deletions app/AuthClientServer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ReactNode } from "react";
import AuthClientProvider from "./AuthClientProvider";

export default function AuthServerProvider({
children,
}: {
children: ReactNode;
}) {
return <AuthClientProvider>{children}</AuthClientProvider>;
}
1 change: 0 additions & 1 deletion app/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// app/auth/page.tsx
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";
import AuthClient from "./AuthClient";
Expand Down
24 changes: 24 additions & 0 deletions app/courses/[courseId]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";
import CourseForm from "@/components/organisation/courses/CourseForm";

export default async function EditCoursePage({
params,
}: {
params: { courseId: string };
}) {
const user = await getAuthUser();
const { courseId } = await params;
if (user?.organisation?.role !== "admin") {
redirect(`/courses/${courseId}`);
}

return (
<div className="max-w-3xl mx-auto bg-white p-6 rounded shadow">
<h2 className="text-2xl font-semibold text-purple-600 mb-4">
Edit Course
</h2>
<CourseForm mode="edit" courseId={courseId} />
</div>
);
}
50 changes: 50 additions & 0 deletions app/courses/[courseId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ReactNode } from "react";
import Link from "next/link";
import { getAuthUser } from "@/lib/auth";

export default async function CourseLayout({
children,
params,
}: {
children: ReactNode;
params: { courseId: string };
}) {
const user = await getAuthUser();
const isAdmin = user?.organisation?.role === "admin";
const { courseId } = await params;

const response = {
id: courseId,
name: "Sample Course", // Replace with actual API call to fetch course details
};
return (
<div className="max-w-4xl mx-auto bg-white p-6 rounded shadow">
<nav className="text-sm mb-4">
<Link href="/courses" className="text-purple-600 hover:underline">
Go to All Courses
</Link>
{/* <span className="mx-2">/</span>
<span className="font-medium">{response.name}</span> */}
</nav>

{isAdmin && (
<div className="text-right mb-6 space-x-2">
<Link
href={`/courses/${response.id}/edit`}
className="px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded"
>
Edit Course Details
</Link>
<Link
href={`/courses/${response.id}/modules/new`}
className="px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded"
>
Add new module
</Link>
</div>
)}

{children}
</div>
);
}
27 changes: 27 additions & 0 deletions app/courses/[courseId]/modules/[moduleId]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";
import ModuleForm from "@/components/organisation/courses/ModuleForm";

export default async function EditModulePage({
params,
}: {
params: { courseId: string; moduleId: string };
}) {
const user = await getAuthUser();
const { courseId, moduleId } = await params;
if (!courseId || !moduleId) {
redirect("/courses");
}
if (user?.organisation?.role !== "admin") {
redirect(`/${courseId}/modules`);
}

return (
<div className="max-w-3xl mx-auto bg-white p-6 rounded shadow">
<h2 className="text-2xl font-semibold text-purple-600 mb-4">
Edit Module
</h2>
<ModuleForm mode="edit" courseId={courseId} moduleId={moduleId} />
</div>
);
}
18 changes: 18 additions & 0 deletions app/courses/[courseId]/modules/[moduleId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// app/courses/[courseId]/modules/[moduleId]/page.tsx
import ModuleDetail, {
ModuleDetailData,
} from "@/components/organisation/courses/ModuleDetail";

export default async function ModulePage({
params,
}: {
params: { courseId: string; moduleId: string };
}) {
const { moduleId } = await params;

return (
<div className="max-w-3xl mx-auto bg-white p-6 rounded shadow">
<ModuleDetail moduleId={moduleId} />
</div>
);
}
27 changes: 27 additions & 0 deletions app/courses/[courseId]/modules/new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";
import ModuleForm from "@/components/organisation/courses/ModuleForm";

export default async function NewModulePage({
params,
}: {
params: { courseId: string };
}) {
const user = await getAuthUser();
const { courseId } = await params;
if (!courseId) {
redirect("/courses");
}
if (user?.organisation?.role !== "admin") {
redirect(`/${courseId}/modules`);
}

return (
<div className="max-w-3xl mx-auto bg-white p-6 rounded shadow">
<h2 className="text-2xl font-semibold text-purple-600 mb-4">
Create New Module
</h2>
<ModuleForm mode="create" courseId={courseId} />
</div>
);
}
14 changes: 14 additions & 0 deletions app/courses/[courseId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ModuleList from "@/components/organisation/courses/ModuleList";
import { getAuthUser } from "@/lib/auth";

export default async function CoursePage({
params,
}: {
params: { courseId: string };
}) {
const user = await getAuthUser();
const isAdmin = user?.organisation?.role === "admin";
const { courseId } = await params;

return <ModuleList courseId={courseId} isEditMode={isAdmin} />;
}
11 changes: 11 additions & 0 deletions app/courses/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// app/courses/layout.tsx
import { ReactNode } from "react";

export default function CoursesLayout({ children }: { children: ReactNode }) {
return (
<div className="min-h-screen bg-white p-6">
<h1 className="text-4xl font-bold text-purple-600 mb-8">Courses</h1>
{children}
</div>
);
}
20 changes: 20 additions & 0 deletions app/courses/new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// app/courses/new/page.tsx
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";
import CourseForm from "@/components/organisation/courses/CourseForm";

export default async function NewCoursePage() {
const user = await getAuthUser();
if (user?.organisation?.role !== "admin") {
redirect("/courses");
}

return (
<div className="max-w-3xl mx-auto bg-white p-6 rounded shadow">
<h2 className="text-2xl font-semibold text-purple-600 mb-4">
Create New Course
</h2>
<CourseForm mode="create" />
</div>
);
}
42 changes: 36 additions & 6 deletions app/courses/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
"use client";

import CourseList from "@/components/organisation/courses/CourseList";
import { Course } from "@/components/organisation/courses/CourseCard";
import { useAuth } from "@/context/AuthContext";
import { useState, useEffect } from "react";

export default function CoursesPage() {
return (
<div>
<h1>Courses</h1>
<p>List of courses will be displayed here.</p>
</div>
);
const { user } = useAuth();
const [courses, setCourses] = useState<Course[]>([]);

if (!user || !user.hasCompletedOnboarding) {
return null;
}

const isAdmin = user?.organisation?.role === "admin";

useEffect(() => {
// Fetch courses from API or database
async function fetchCourses() {
// Replace with actual API call
const fetchedCourses = await fetch("/api/courses", {
credentials: "include",
})
.then((response) => response.json())
.then((data) => data["courses"] || [])
.catch((error) => {
console.error("Failed to fetch courses:", error);
return [];
});
setCourses(fetchedCourses);
}
fetchCourses();
}, []);

// 3) Render the client component that will show/hide admin buttons
return <CourseList courses={courses} isAdmin={isAdmin} />;
}
4 changes: 2 additions & 2 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";

export default async function DashboardPage() {
const user = await getAuthUser();
if (!user) {
if (!user || !user.hasCompletedOnboarding) {
return null;
}

Expand All @@ -12,6 +11,7 @@ export default async function DashboardPage() {
<h1>Welcome, {user.firstname || user.email}</h1>
<p>Organisation: {user.organisation?.organisationname}</p>
<p>Role: {user.organisation.role}</p>
<p>Dashboard is currently in progress.</p>
</div>
);
}
5 changes: 4 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
import "./globals.css";
import { AuthProvider } from "@/context/AuthContext";
import LayoutClient from "./layoutClient";
import AuthServerProvider from "./AuthClientServer";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -20,7 +21,9 @@ export default async function RootLayout({
<html lang="en">
<body className={`${inter.className} bg-white`}>
<AuthProvider>
<LayoutClient>{children}</LayoutClient>
<AuthServerProvider>
<LayoutClient>{children}</LayoutClient>
</AuthServerProvider>
</AuthProvider>
</body>
</html>
Expand Down
17 changes: 0 additions & 17 deletions app/layoutClient.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"use client";

import { useAuth } from "@/context/AuthContext";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import HeaderNav from "@/components/HeaderNav";
import SideNav from "@/components/SideNav";
import OrgNav from "@/components/organisation/OrgNav";
Expand All @@ -16,24 +14,9 @@ export default function LayoutClient({
}) {
const { user } = useAuth();
const isLoggedIn = user && user.isLoggedIn;
const router = useRouter();
const role = user && user.hasCompletedOnboarding && user.organisation?.role;
const isAdmin = role === "admin";

const shouldRedirectToOnboarding = isLoggedIn && !user.hasCompletedOnboarding;

useEffect(() => {
if (shouldRedirectToOnboarding) {
router.push("/onboarding");
}
}, [shouldRedirectToOnboarding, router]);

useEffect(() => {
if (!isLoggedIn) {
router.push("/auth");
}
}, [isLoggedIn, router]);

return (
<div>
{isLoggedIn && user.hasCompletedOnboarding && isAdmin && (
Expand Down
Loading