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
109 changes: 89 additions & 20 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createTheme, MantineProvider } from "@mantine/core";
import "@mantine/core/styles.css";
import "@mantine/notifications/styles.css";
import { Route, Routes, Navigate, useLocation } from "react-router-dom";
import PropTypes from "prop-types";
import { Notifications } from "@mantine/notifications";
import { Layout } from "./components/layout";
import Dashboard from "./Modules/Dashboard/dashboardNotifications";
Expand All @@ -15,6 +16,7 @@ import InactivityHandler from "./helper/inactivityhandler";
import Examination from "./Modules/Examination/examination";
import Database from "./Modules/Database/database";
import ProgrammeCurriculumRoutes from "./Modules/Program_curriculum/programmCurriculum";
import PatentModulePage from "./Modules/Patent/PatentModulePage";
import NotFoundPage from "./components/NotFoundPage";

const theme = createTheme({
Expand All @@ -28,60 +30,127 @@ const theme = createTheme({
},
});

function RequireAuth({ children }) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasToken is captured once at mount and goes stale. Boolean(localStorage.getItem('authToken')) is evaluated when App first renders. If the token is added or removed later in the session (e.g. after login without a full page reload), hasToken keeps its initial value and <ValidateAuth /> / <InactivityHandler /> will behave incorrectly. Prefer deriving this from Redux auth state so it re-renders reactively.

const currentLocation = useLocation();
const token = localStorage.getItem("authToken");

if (!token) {
return (
<Navigate
to="/accounts/login"
replace
state={{ from: currentLocation.pathname }}
/>
);
}

return children;
}

RequireAuth.propTypes = {
children: PropTypes.node.isRequired,
};

export default function App() {
const location = useLocation();
const hasToken = Boolean(localStorage.getItem("authToken"));

return (
<MantineProvider theme={theme}>
<Notifications position="top-center" autoClose={2000} limit={1} />
{location.pathname !== "/accounts/login" && <ValidateAuth />}
{location.pathname !== "/accounts/login" && <InactivityHandler />}
{location.pathname !== "/accounts/login" && hasToken && <ValidateAuth />}
{location.pathname !== "/accounts/login" && hasToken && (
<InactivityHandler />
)}

<Routes>
<Route path="/" element={<Navigate to="/accounts/login" replace />} />
<Route
path="/"
element={
<Navigate
to={hasToken ? "/dashboard" : "/accounts/login"}
replace
/>
}
/>
<Route
path="/dashboard"
element={
<Layout>
<Dashboard />
</Layout>
<RequireAuth>
<Layout>
<Dashboard />
</Layout>
</RequireAuth>
}
/>
<Route
path="/academics"
element={
<Layout>
<AcademicPage />
</Layout>
<RequireAuth>
<Layout>
<AcademicPage />
</Layout>
</RequireAuth>
}
/>
<Route
path="/profile"
element={
<Layout>
<Profile />
</Layout>
<RequireAuth>
<Layout>
<Profile />
</Layout>
</RequireAuth>
}
/>
<Route
path="/facultyprofessionalprofile/*"
element={
<Layout>
<FacultyProfessionalProfile />
</Layout>
<RequireAuth>
<Layout>
<FacultyProfessionalProfile />
</Layout>
</RequireAuth>
}
/>
<Route
path="/programme_curriculum/*"
element={
<div>
<ProgrammeCurriculumRoutes />
</div>
<RequireAuth>
<div>
<ProgrammeCurriculumRoutes />
</div>
</RequireAuth>
}
/>
<Route
path="/patentsystem/*"
element={
<RequireAuth>
<Layout>
<PatentModulePage />
</Layout>
</RequireAuth>
}
/>
<Route path="/accounts/login" element={<LoginPage />} />
<Route path="/reset-password" element={<ForgotPassword />} />
<Route path="/examination/*" element={<Examination />} />
<Route path="/database/*" element={<Database />} />
<Route
path="/examination/*"
element={
<RequireAuth>
<Examination />
</RequireAuth>
}
/>
<Route
path="/database/*"
element={
<RequireAuth>
<Database />
</RequireAuth>
}
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</MantineProvider>
Expand Down
31 changes: 31 additions & 0 deletions src/Modules/Patent/PatentModulePage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useSelector } from "react-redux";
import ApplicantMainDashboard from "./components/Applicant/ApplicantMainDashboard";
import DirectorMainDashboard from "./components/Director/DirectorMainDashboard";
import PCCAdminMainDashboard from "./components/PCCAdmin/PCCAdminMainDashboard";

export default function PatentModulePage() {
const role = useSelector((state) => state.user.role);

if (
[
"student",
"alumini",
"Professor",
"Associate Professor",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "alumini" should be "alumni". This will break role matching if the backend sends "alumni".

"Assistant Professor",
"Research Engineer",
].includes(role)
) {
return <ApplicantMainDashboard />;
}

if (role === "Director") {
return <DirectorMainDashboard />;
}

if (role === "PCC Admin") {
return <PCCAdminMainDashboard />;
}

return null;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attorney role is missing and unreachable. This is the component actually wired into App.jsx via <PatentModulePage />, but there is no branch for role === "Attorney". Attorney users will always hit the return null at the bottom.

The Attorney branch does exist in src/Modules/Patent/pages/PatentModulePage.jsx, but that file is only used inside PatentRoutes.jsx — which is never imported anywhere in the app.

Fix: Either add the Attorney branch here, or wire PatentRoutes.jsx into App.jsx and delete this root-level duplicate.

109 changes: 109 additions & 0 deletions src/Modules/Patent/components/Applicant/ApplicantMainDashboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { useEffect, useState } from "react";
import { Grid, Container, Loader, Flex, Select } from "@mantine/core";
import { useDispatch } from "react-redux";
import { SortAscending } from "@phosphor-icons/react";
import CustomBreadcrumbs from "../../../../components/Breadcrumbs.jsx";
import ModuleTabs from "../../../../components/moduleTabs.jsx";
import SubmitNewApplication from "./SubmitNewApplication/ApplicantSubmit.jsx";
import ApplicantDashboard from "./Dashboard/ApplicantDashboard.jsx";
import ViewApplicationsPage from "./ViewApplication/ApplicationView.jsx";
import SavedDraftsPage from "./SavedDrafts/ApplicationDraft.jsx";
import NotificationsPage from "./Notifications/ApplicantNotifications.jsx";
import ApplicationForm from "./SubmitNewApplication/ApplicationForm.jsx";

const categories = ["Most Recent", "Tags", "Title"];

function ApplicantMainDashboard() {
const [activeTab, setActiveTab] = useState("0");
const [sortedBy, setSortedBy] = useState("Most Recent");
const [loading, setLoading] = useState(false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dispatch imported but never used. useDispatch() is called here and dispatch is even in the useEffect dependency array, but dispatch is never actually called anywhere in this component. Remove useDispatch import and this declaration.

const dispatch = useDispatch();

// Define your tabs here
const tabItems = [
{ title: "Dashboard" },
{ title: "Submit New Application" },
{ title: "View Applications" },
{ title: "Saved Drafts" },
{ title: "Notifications" },
];

useEffect(() => {
const fetchData = async () => {
const token = localStorage.getItem("authToken");
if (!token) return console.error("No authentication token found!");

try {
setLoading(true);
// Fetch data logic here if needed
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead useEffect — placeholder never implemented. fetchData toggles loading on and off but never fetches anything. The // Fetch data logic here if needed comment signals this was left as a stub. Either implement the actual data fetch or remove the whole useEffect block.

};

fetchData();
}, [dispatch]);

return (
<>
<CustomBreadcrumbs />
<Flex justify="space-between" align="center" mt="lg">
<ModuleTabs
tabs={tabItems}
activeTab={activeTab}
setActiveTab={setActiveTab}
badges={[]}
/>

<Flex align="center" mt="md" rowGap="1rem" columnGap="4rem" wrap="wrap">
<Select
classNames={{
option: "select-options",
input: "select-inputs",
}}
variant="filled"
leftSection={<SortAscending />}
data={categories}
value={sortedBy}
onChange={setSortedBy}
placeholder="Sort By"
/>
</Flex>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort dropdown has no effect on data. sortedBy state is updated by the <Select> but is never used to sort or filter any list. Either implement the sort logic or remove the dropdown until it is ready.

</Flex>

{/* Render content based on the active tab */}
<Grid mt="xl">
{loading ? (
<Container py="xl">
<Loader size="lg" />
</Container>
) : (
<>
{activeTab === "0" && (
<ApplicantDashboard setActiveTab={setActiveTab} />
)}
{activeTab === "1" && (
<SubmitNewApplication setActiveTab={setActiveTab} />
)}
{activeTab === "1.1" && (
<ApplicationForm setActiveTab={setActiveTab} />
)}
{activeTab === "2" && (
<ViewApplicationsPage setActiveTab={setActiveTab} />
)}
{activeTab === "3" && (
<SavedDraftsPage setActiveTab={setActiveTab} />
)}
{activeTab === "4" && (
<NotificationsPage setActiveTab={setActiveTab} />
)}
</>
)}
</Grid>
</>
);
}

export default ApplicantMainDashboard;
Loading
Loading