diff --git a/app/AuthClientProvider.tsx b/app/AuthClientProvider.tsx
new file mode 100644
index 0000000..e64460e
--- /dev/null
+++ b/app/AuthClientProvider.tsx
@@ -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}>;
+}
diff --git a/app/AuthClientServer.tsx b/app/AuthClientServer.tsx
new file mode 100644
index 0000000..ddd2e38
--- /dev/null
+++ b/app/AuthClientServer.tsx
@@ -0,0 +1,10 @@
+import { ReactNode } from "react";
+import AuthClientProvider from "./AuthClientProvider";
+
+export default function AuthServerProvider({
+ children,
+}: {
+ children: ReactNode;
+}) {
+ return {children};
+}
diff --git a/app/auth/page.tsx b/app/auth/page.tsx
index 3d7e6c7..efa5091 100644
--- a/app/auth/page.tsx
+++ b/app/auth/page.tsx
@@ -1,4 +1,3 @@
-// app/auth/page.tsx
import { redirect } from "next/navigation";
import { getAuthUser } from "@/lib/auth";
import AuthClient from "./AuthClient";
diff --git a/app/courses/[courseId]/edit/page.tsx b/app/courses/[courseId]/edit/page.tsx
new file mode 100644
index 0000000..99b32c9
--- /dev/null
+++ b/app/courses/[courseId]/edit/page.tsx
@@ -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 (
+
+
+ Edit Course
+
+
+
+ );
+}
diff --git a/app/courses/[courseId]/layout.tsx b/app/courses/[courseId]/layout.tsx
new file mode 100644
index 0000000..5c27e2b
--- /dev/null
+++ b/app/courses/[courseId]/layout.tsx
@@ -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 (
+
+
+
+ {isAdmin && (
+
+
+ Edit Course Details
+
+
+ Add new module
+
+
+ )}
+
+ {children}
+
+ );
+}
diff --git a/app/courses/[courseId]/modules/[moduleId]/edit/page.tsx b/app/courses/[courseId]/modules/[moduleId]/edit/page.tsx
new file mode 100644
index 0000000..dd6b95e
--- /dev/null
+++ b/app/courses/[courseId]/modules/[moduleId]/edit/page.tsx
@@ -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 (
+
+
+ Edit Module
+
+
+
+ );
+}
diff --git a/app/courses/[courseId]/modules/[moduleId]/page.tsx b/app/courses/[courseId]/modules/[moduleId]/page.tsx
new file mode 100644
index 0000000..cc8e5b2
--- /dev/null
+++ b/app/courses/[courseId]/modules/[moduleId]/page.tsx
@@ -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 (
+
+
+
+ );
+}
diff --git a/app/courses/[courseId]/modules/new/page.tsx b/app/courses/[courseId]/modules/new/page.tsx
new file mode 100644
index 0000000..e11a5c1
--- /dev/null
+++ b/app/courses/[courseId]/modules/new/page.tsx
@@ -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 (
+
+
+ Create New Module
+
+
+
+ );
+}
diff --git a/app/courses/[courseId]/page.tsx b/app/courses/[courseId]/page.tsx
new file mode 100644
index 0000000..3f8f466
--- /dev/null
+++ b/app/courses/[courseId]/page.tsx
@@ -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 ;
+}
diff --git a/app/courses/layout.tsx b/app/courses/layout.tsx
new file mode 100644
index 0000000..675d669
--- /dev/null
+++ b/app/courses/layout.tsx
@@ -0,0 +1,11 @@
+// app/courses/layout.tsx
+import { ReactNode } from "react";
+
+export default function CoursesLayout({ children }: { children: ReactNode }) {
+ return (
+
+
Courses
+ {children}
+
+ );
+}
diff --git a/app/courses/new/page.tsx b/app/courses/new/page.tsx
new file mode 100644
index 0000000..f47e241
--- /dev/null
+++ b/app/courses/new/page.tsx
@@ -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 (
+
+
+ Create New Course
+
+
+
+ );
+}
diff --git a/app/courses/page.tsx b/app/courses/page.tsx
index 29a7f48..7ab03f0 100644
--- a/app/courses/page.tsx
+++ b/app/courses/page.tsx
@@ -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 (
-
-
Courses
-
List of courses will be displayed here.
-
- );
+ const { user } = useAuth();
+ const [courses, setCourses] = useState([]);
+
+ 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 ;
}
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index 486d7a8..da6092a 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -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;
}
@@ -12,6 +11,7 @@ export default async function DashboardPage() {
Welcome, {user.firstname || user.email}
Organisation: {user.organisation?.organisationname}
Role: {user.organisation.role}
+ Dashboard is currently in progress.
);
}
diff --git a/app/layout.tsx b/app/layout.tsx
index c0e726d..f02a20c 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -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"] });
@@ -20,7 +21,9 @@ export default async function RootLayout({
- {children}
+
+ {children}
+
diff --git a/app/layoutClient.tsx b/app/layoutClient.tsx
index f780c94..47e4591 100644
--- a/app/layoutClient.tsx
+++ b/app/layoutClient.tsx
@@ -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";
@@ -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 (
{isLoggedIn && user.hasCompletedOnboarding && isAdmin && (
diff --git a/app/onboarding/page.tsx b/app/onboarding/page.tsx
index 293187c..db0a5c9 100644
--- a/app/onboarding/page.tsx
+++ b/app/onboarding/page.tsx
@@ -8,9 +8,13 @@ export default function OnboardingPage() {
const router = useRouter();
const { user, setUser } = useAuth();
+ if (!user || !user.isLoggedIn) {
+ return null;
+ }
+
const [role, setRole] = useState<"admin" | "employee">("employee");
const [orgName, setOrgName] = useState("");
- const [orgId, setOrgId] = useState("");
+ const [orgInvite, setOrgInvite] = useState("");
const [error, setError] = useState
(null);
const [loading, setLoading] = useState(false);
@@ -37,7 +41,9 @@ export default function OnboardingPage() {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ organisationId: orgId }),
+ body: JSON.stringify({
+ inviteCode: orgInvite,
+ }),
});
if (!addemp.ok) {
const body = await addemp.json().catch(() => ({}));
@@ -45,7 +51,6 @@ export default function OnboardingPage() {
}
}
- // 2) Mark onboarding complete
const done = await fetch("/api/complete-onboarding", {
method: "POST",
credentials: "include",
@@ -137,8 +142,8 @@ export default function OnboardingPage() {
setOrgId(e.target.value)}
+ value={orgInvite}
+ onChange={(e) => setOrgInvite(e.target.value)}
required
className="w-full border border-gray-300 rounded-md p-2 focus:outline-none focus:ring-2 focus:ring-purple-300"
/>
diff --git a/app/settings/page.tsx b/app/settings/page.tsx
index 2d5771f..a6b1030 100644
--- a/app/settings/page.tsx
+++ b/app/settings/page.tsx
@@ -1,8 +1,35 @@
-export default function SettingsPage() {
+import { getAuthUser } from "@/lib/auth";
+import OrgSettings from "@/components/organisation/settings/OrgSettings";
+
+export default async function SettingsPage() {
+ const user = await getAuthUser();
+ if (!user || !user.hasCompletedOnboarding) {
+ return null;
+ }
+ const organisation = user.organisation;
+ const role = organisation.role;
+ const isAdmin = role === "admin";
+ const isMember = role === "employee";
+
+ if (isAdmin) {
+ return (
+
+
+
+ );
+ }
+
+ if (isMember) {
+ return (
+
+
Member Settings
+
+ );
+ }
+
return (
-
Settings
-
Settings page content goes here.
+
Page should not reach here
);
}
diff --git a/app/users/layout.tsx b/app/users/layout.tsx
new file mode 100644
index 0000000..1c4aaa3
--- /dev/null
+++ b/app/users/layout.tsx
@@ -0,0 +1,10 @@
+import { ReactNode } from "react";
+
+export default function UsersLayout({ children }: { children: ReactNode }) {
+ return (
+
+
Users
+ {children}
+
+ );
+}
diff --git a/app/users/page.tsx b/app/users/page.tsx
new file mode 100644
index 0000000..f287a12
--- /dev/null
+++ b/app/users/page.tsx
@@ -0,0 +1,17 @@
+import { getAuthUser } from "@/lib/auth";
+import { redirect } from "next/navigation";
+import UsersList from "@/components/organisation/users/UsersList";
+
+export default async function UsersPage() {
+ const user = await getAuthUser();
+ if (!user || !user.hasCompletedOnboarding) {
+ return null;
+ }
+ const isAdmin = user?.organisation?.role === "admin";
+ if (!isAdmin) {
+ redirect("/dashboard");
+ return null;
+ }
+
+ return ;
+}
diff --git a/components/SideNav.tsx b/components/SideNav.tsx
index 1b6783d..6d38d41 100644
--- a/components/SideNav.tsx
+++ b/components/SideNav.tsx
@@ -45,7 +45,7 @@ export default function SideNav() {
const [imgSrc, setImgSrc] = useState(avatarUrl);
return (
-