Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
myapp
cmd.md
cmd.md
.env
40 changes: 32 additions & 8 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "react-router-dom";
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/react";

import Navbar from "./components/layout/Navbar";
import LandingPage from "./components/layout/LandingPage";
import AuthForm from "./components/auth/AuthForm";
Expand Down Expand Up @@ -43,6 +44,7 @@ import ScrollToTop from "./components/ui/ScrollToTop";
import ScrollToTopOnRouteChange from "./components/ui/ScrollToTopOnRouteChange";
import { isAuthenticated } from "./utils/auth";
import { SocketProvider } from "./contexts/SocketContext";
import Dashboard from "./pages/Dashboard";

interface AppProps {}

Expand Down Expand Up @@ -99,8 +101,20 @@ const App: React.FC<AppProps> = () => {
<div className="min-h-screen bg-royal-black">
<ScrollToTopOnRouteChange />
<Navbar authenticated={isLoggedIn} onAuthChange={checkAuthStatus} />

<Routes>
<Route path="/" element={<LandingPage />} />

{/* ✅ Dashboard PROTECTED */}
<Route
path="/dashboard"
element={
<ProtectedRoute authenticated={isLoggedIn}>
<Dashboard />
</ProtectedRoute>
}
/>

<Route
path="/auth"
element={
Expand All @@ -109,18 +123,17 @@ const App: React.FC<AppProps> = () => {
</AuthRoute>
}
/>

<Route path="/auth/callback" element={<AuthCallback />} />
<Route
path="/auth/complete-profile"
element={<CompleteProfile />}
/>
<Route path="/auth/complete-profile" element={<CompleteProfile />} />
<Route path="/pdfs" element={<PDFsPage />} />
<Route path="/ebooks" element={<EbooksPage />} />
<Route
path="/interview-resources"
element={<InterviewResourcesPage />}
/>
<Route path="/forgot-password" element={<ForgotPasswordForm />} />

<Route
path="/change-username"
element={
Expand All @@ -129,10 +142,13 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route path="/discussions" element={<DiscussionsPage />} />
<Route path="/discussions/:id" element={<DiscussionDetailPage />} />

<Route path="/courses" element={<CoursesPage />} />
<Route path="/courses/:id" element={<CourseDetailPage />} />

<Route
path="/courses/:courseId/test/:testId"
element={
Expand All @@ -141,6 +157,7 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route
path="/courses/:courseId/test/:testId/processing"
element={
Expand All @@ -149,6 +166,7 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route
path="/courses/:courseId/test/:testId/results"
element={
Expand All @@ -157,14 +175,13 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route path="/roadmaps" element={<RoadmapsPage />} />
<Route path="/roadmaps/:id" element={<RoadmapDetailPage />} />
<Route path="/pdf-chatbot" element={<PdfChatbotPage />} />
<Route
path="/suggest-feature"
element={<FeatureSuggestionPage />}
/>
<Route path="/suggest-feature" element={<FeatureSuggestionPage />} />
<Route path="/report-bug" element={<BugReportPage />} />

<Route
path="/admin"
element={
Expand All @@ -173,6 +190,7 @@ const App: React.FC<AppProps> = () => {
</AdminRoute>
}
/>

<Route
path="/discussions/new"
element={
Expand All @@ -181,6 +199,7 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route
path="/courses/create"
element={
Expand All @@ -189,6 +208,7 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route
path="/roadmaps/create"
element={
Expand All @@ -197,6 +217,7 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route
path="/upload"
element={
Expand All @@ -205,12 +226,15 @@ const App: React.FC<AppProps> = () => {
</ProtectedRoute>
}
/>

<Route
path="/verify-certificate"
element={<CertificateVerificationPage />}
/>

<Route path="*" element={<NotFoundPage />} />
</Routes>

<Footer />
<ScrollToTop />
<Analytics />
Expand Down
13 changes: 13 additions & 0 deletions client/src/components/dashboard/ProfileOverview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const ProfileOverview = () => {
return (
<div>
<img src="https://via.placeholder.com/80" alt="avatar" />
<h2>Username</h2>
<p>email@example.com</p>
<p>Member since: Jan 2024</p>
<button>Edit Profile</button>
</div>
);
};

export default ProfileOverview;
13 changes: 13 additions & 0 deletions client/src/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import ProfileOverview from "../components/dashboard/ProfileOverview";


const Dashboard = () => {
return (
<div style={{ padding: "20px" }}>
<h1>Dashboard</h1>
<ProfileOverview />
</div>
);
};

export default Dashboard;
9 changes: 9 additions & 0 deletions client/src/services/dashboardService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import api from "../utils/api";



// PR-1: Dashboard Profile
export const getDashboardProfile = async () => {
const response = await api.get("/users/me");
return response.data;
};
2 changes: 2 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ app.use(
app.use(cookieParser());
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ extended: true, limit: "50mb" }));
import userRoutes from "./routes/users.js";
app.use("/api/users", userRoutes);

// Initialize Passport
app.use(passport.initialize());
Expand Down
31 changes: 31 additions & 0 deletions server/routes/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import jwt from "jsonwebtoken";
import prisma from "../db.js";

const requireAuth = async (req, res, next) => {
try {
const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ message: "Unauthorized" });
}

const token = authHeader.split(" ")[1];

const decoded = jwt.verify(token, process.env.JWT_SECRET);

const user = await prisma.user.findUnique({
where: { id: decoded.id },
});

if (!user) {
return res.status(401).json({ message: "User not found" });
}

req.user = user;
next();
} catch (error) {
return res.status(401).json({ message: "Invalid token" });
}
};

export default requireAuth;