Skip to content

Commit e17bfcd

Browse files
committed
bug fixes
1 parent a00a843 commit e17bfcd

5 files changed

Lines changed: 89 additions & 36 deletions

File tree

src/App.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,27 @@ import CourseTeamDashboard from './pages/CourseTeamDashboard';
2222
import CourseSettings from './pages/CourseSettings';
2323
import Dashboard from './pages/Dashboard';
2424
import ProjectNotFound from './pages/ProjectNotFound';
25+
import NotFound from './pages/NotFound';
2526
import LoginCard from './components/LoginCard';
2627
import Admin from './pages/Admin';
2728

2829
function AppContent() {
2930
const location = useLocation();
31+
32+
// Define all valid route patterns
33+
const isValidRoute =
34+
location.pathname === '/login' ||
35+
location.pathname === '/courses' ||
36+
location.pathname === '/' ||
37+
location.pathname === '/admin' ||
38+
location.pathname.startsWith('/courses/') ||
39+
location.pathname.startsWith('/dashboard/') ||
40+
location.pathname.startsWith('/not-found/');
41+
42+
// Only show NavBar on routes that need it
43+
// Exclude: login, courses, course pages, dashboard pages, admin, not-found pages, and 404 pages
3044
const shouldShowNav =
45+
isValidRoute &&
3146
location.pathname !== '/login' &&
3247
location.pathname !== '/courses' &&
3348
!location.pathname.startsWith('/courses/') &&
@@ -46,15 +61,16 @@ function AppContent() {
4661
<Route path="/courses" element={<Courses />} />
4762
<Route path="/courses/:courseId" element={<CourseLayout />}>
4863
<Route index element={<CourseProjects />} />
49-
<Route path="dashboard" element={<CourseDashboard />} />
5064
<Route path="dashboard/:teamId" element={<CourseTeamDashboard />} />
65+
<Route path="dashboard" element={<CourseDashboard />} />
5166
<Route path="settings" element={<CourseSettings />} />
5267
</Route>
5368
<Route path="/dashboard/:teamId" element={<Dashboard />} />
5469
<Route path="/admin" element={<Admin />} />
5570
<Route path="/not-found/:team_name" element={<ProjectNotFound />} />
5671
<Route path="/login" element={<LoginCard />} />
5772
<Route path="/" element={<Navigate to="/login" replace />} />
73+
<Route path="*" element={<NotFound />} />
5874
</Routes>
5975
</main>
6076
</div>

src/components/CourseLayout.tsx

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { useParams, Outlet, useLocation, useNavigate } from 'react-router-dom';
1+
import {
2+
useParams,
3+
Outlet,
4+
useLocation,
5+
useNavigate,
6+
matchPath,
7+
} from 'react-router-dom';
28
import { useEffect, useState, createContext, useContext, useMemo } from 'react';
39
import { useRoleAccess } from '@/hooks/useRoleAccess';
410
import { CourseNavBar } from '@/components/CourseNavBar';
@@ -76,17 +82,17 @@ export function CourseLayout() {
7682
// This prevents students from having admin privileges after admin sign-out
7783
const hasRoleMismatch = useMemo(() => {
7884
if (!user || !effectiveRole) return false;
79-
85+
8086
// If user is not an admin globally, they should never have ADMIN effectiveRole
8187
if (user.role !== 'ADMIN' && effectiveRole === 'ADMIN') {
8288
return true;
8389
}
84-
90+
8591
// If user is a student globally, they should never have ADMIN effectiveRole
8692
if (user.role === 'STUDENT' && effectiveRole === 'ADMIN') {
8793
return true;
8894
}
89-
95+
9096
return false;
9197
}, [user, effectiveRole]);
9298

@@ -130,36 +136,35 @@ export function CourseLayout() {
130136
courseId,
131137
]);
132138

139+
// Only redirect from settings if user doesn't have access
140+
// Let React Router handle all other route matching - no redirects for dashboard routes
133141
useEffect(() => {
134142
if (!courseId) return;
135-
if (loading) return;
136-
if (!effectiveRole) return;
137143

138-
const basePath = `/courses/${courseId}`;
139-
const normalizedPath = location.pathname.replace(/\/$/, '');
144+
const currentPath = location.pathname;
140145

141-
const allowedPaths = new Set<string>([basePath]);
142-
143-
// Allow dashboard routes for all roles (dashboard/:teamId)
144-
allowedPaths.add(`${basePath}/dashboard`);
145-
146-
if (effectiveRole === 'INSTRUCTOR' || effectiveRole === 'ADMIN') {
147-
allowedPaths.add(`${basePath}/settings`);
146+
// Never redirect from dashboard routes - let React Router handle them
147+
if (currentPath.includes('/dashboard')) {
148+
return;
148149
}
149150

150-
const normalizedAllowed = Array.from(allowedPaths).map((path) =>
151-
path.replace(/\/$/, '')
151+
// Only check settings route access
152+
const settingsMatch = matchPath(
153+
{ path: '/courses/:courseId/settings', end: true },
154+
currentPath
152155
);
153156

154-
// Check if path matches exactly or starts with an allowed path (for nested routes like dashboard/:teamId)
155-
const isAllowed = normalizedAllowed.some((path) =>
156-
path === normalizedPath || normalizedPath.startsWith(path + '/')
157-
);
157+
if (settingsMatch?.params.courseId === courseId) {
158+
// Wait for loading to complete and effectiveRole to be set before checking settings access
159+
if (loading) return;
160+
if (!effectiveRole) return;
158161

159-
if (!isAllowed) {
160-
navigate(basePath, { replace: true });
162+
// Only redirect from settings if user doesn't have access
163+
if (effectiveRole !== 'INSTRUCTOR' && effectiveRole !== 'ADMIN') {
164+
navigate(`/courses/${courseId}`, { replace: true });
165+
}
161166
}
162-
}, [courseId, effectiveRole, location.pathname, navigate, loading]);
167+
}, [courseId, effectiveRole, navigate, loading, location.pathname]);
163168

164169
const toggleViewAsStudent = () => {
165170
if (!isAdmin) return;

src/pages/CourseDashboard.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,8 @@ import { ArrowLeft } from 'lucide-react';
66
import { useEffect } from 'react';
77

88
export default function CourseDashboard() {
9-
const { courseId } = useParams<{ courseId: string }>();
109
const navigate = useNavigate();
11-
const { offering, loading: offeringLoading, effectiveRole } = useCourseContext();
12-
13-
// Check course-specific role access after fetching offering
14-
useEffect(() => {
15-
if (effectiveRole && effectiveRole !== 'STUDENT') {
16-
// Redirect to projects page if not a student
17-
navigate(`/courses/${courseId}`, { replace: true });
18-
}
19-
}, [effectiveRole, courseId, navigate]);
10+
const { offering, loading: offeringLoading } = useCourseContext();
2011

2112
return (
2213
<div className="container mx-auto p-6">

src/pages/CourseTeamDashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export default function CourseTeamDashboard() {
4141
const canManage = effectiveRole === 'ADMIN' || effectiveRole === 'INSTRUCTOR';
4242

4343
// Check if user is a member of this team
44-
const isTeamMember = myTeams?.some((t) => t.id === teamIdNum) ?? false;
44+
const isTeamMember = (myTeams?.some((t) => t.id === teamIdNum) ?? false) || canManage;
4545

4646
useEffect(() => {
4747
const expectedPath = `/courses/${courseId}/dashboard/${teamIdNum}`;

src/pages/NotFound.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useNavigate } from 'react-router-dom';
2+
import { Button } from '@/components/ui/button';
3+
import { AlertCircle, ArrowLeft } from 'lucide-react';
4+
5+
export default function NotFound() {
6+
const navigate = useNavigate();
7+
8+
const handleGoToCourses = () => {
9+
navigate('/courses');
10+
};
11+
12+
return (
13+
<div className="flex min-h-screen bg-gray-50 items-center justify-center p-6">
14+
<div className="max-w-md w-full text-center space-y-6">
15+
<div className="flex justify-center">
16+
<div className="rounded-full bg-gray-100 p-4">
17+
<AlertCircle className="h-12 w-12 text-gray-400" />
18+
</div>
19+
</div>
20+
21+
<div className="space-y-2">
22+
<h1 className="text-3xl font-bold text-gray-900">Page Not Found</h1>
23+
<p className="text-gray-600">
24+
The page you're looking for doesn't exist.
25+
</p>
26+
</div>
27+
28+
<div className="pt-4">
29+
<Button
30+
onClick={handleGoToCourses}
31+
className="bg-black hover:bg-gray-800 text-white flex items-center gap-2 mx-auto"
32+
>
33+
<ArrowLeft className="h-4 w-4" />
34+
<span>Go Back to Courses</span>
35+
</Button>
36+
</div>
37+
</div>
38+
</div>
39+
);
40+
}
41+

0 commit comments

Comments
 (0)