diff --git a/__tests__/components/navbar.test.jsx b/__tests__/components/navbar.test.jsx index 33f684deb..81cb405a6 100644 --- a/__tests__/components/navbar.test.jsx +++ b/__tests__/components/navbar.test.jsx @@ -2,6 +2,7 @@ import Navbar from '../../components/navbar'; import React from 'react'; import { SessionProvider } from 'next-auth/react'; import renderer from 'react-test-renderer'; +import Link from 'next/link'; describe('Navbar rendering correctly', () => { it('renders correctly', () => { @@ -14,4 +15,82 @@ describe('Navbar rendering correctly', () => { .toJSON(); expect(tree).toMatchSnapshot(); }); + + it('renders Classes link as "Classes" for non-admin session', () => { + const tree = renderer + .create( + + +
+ Classes +
+
+
+ ) + .toJSON(); + + const jsonString = JSON.stringify(tree); + expect(jsonString).toContain('Classes'); + expect(jsonString).not.toContain('Dashboard'); + }); + + it('renders Classes link as "Dashboard" for ADMIN session', () => { + const tree = renderer + .create( + + +
+ Classes +
+
+
+ ) + .toJSON(); + + const jsonString = JSON.stringify(tree); + expect(jsonString).toContain('Dashboard'); + expect(jsonString).not.toContain('Classes'); + }); + + it('hides Classes link for STUDENT session', () => { + const tree = renderer + .create( + + +
+ Classes +
+
+
+ ) + .toJSON(); + + const jsonString = JSON.stringify(tree); + expect(jsonString).not.toContain('Classes'); + expect(jsonString).not.toContain('Dashboard'); + }); + + it('hides Classes link for unauthenticated session', () => { + const tree = renderer + .create( + + +
+ Classes +
+
+
+ ) + .toJSON(); + + const jsonString = JSON.stringify(tree); + expect(jsonString).not.toContain('Classes'); + expect(jsonString).not.toContain('Dashboard'); + }); }); diff --git a/components/navbar.js b/components/navbar.js index 9d4c44adb..079949256 100644 --- a/components/navbar.js +++ b/components/navbar.js @@ -1,9 +1,61 @@ import Image from 'next/legacy/image'; import Link from 'next/link'; import React from 'react'; +import { useSession } from 'next-auth/react'; import AuthButton from '../components/authButton'; +function updateClassesLinkLabel(child, isAdmin) { + if (!child) return child; + + if (React.isValidElement(child)) { + const isClassesLink = + child.props.href === '/classes' && child.props.children === 'Classes'; + + if (isClassesLink) { + return React.cloneElement(child, { + children: isAdmin ? 'Dashboard' : 'Classes' + }); + } + + if (child.props.children) { + const newChildren = React.Children.map(child.props.children, c => + updateClassesLinkLabel(c, isAdmin) + ); + return React.cloneElement(child, { children: newChildren }); + } + } + + return child; +} + +function hasClassesLink(child) { + if (!child) return false; + if (React.isValidElement(child)) { + if (child.props.href === '/classes') { + return true; + } + if (child.props.children) { + return React.Children.toArray(child.props.children).some(hasClassesLink); + } + } + return false; +} + export default function Navbar({ children }) { + const { data: session } = useSession(); + const role = session?.user?.role; + const hasAccess = role === 'ADMIN' || role === 'TEACHER'; + const isAdmin = role === 'ADMIN'; + + const processedChildren = React.Children.toArray(children) + .filter(child => { + if (hasClassesLink(child)) { + return hasAccess; + } + return true; + }) + .map(child => updateClassesLinkLabel(child, isAdmin)); + return (
@@ -20,7 +72,7 @@ export default function Navbar({ children }) { >
- {React.Children.toArray(children).map(child => ( + {processedChildren.map(child => (
{child}
diff --git a/pages/api/auth/[...nextauth].js b/pages/api/auth/[...nextauth].js index 4895ac83c..27376ddf1 100644 --- a/pages/api/auth/[...nextauth].js +++ b/pages/api/auth/[...nextauth].js @@ -28,6 +28,13 @@ export const authOptions = { // Allows callback URLs on the same origin else if (new URL(url).origin === baseUrl) return url; return baseUrl; + }, + async session({ session, user }) { + if (session?.user && user) { + session.user.role = user.role; + session.user.id = user.id; + } + return session; } } };