Skip to content
3 changes: 1 addition & 2 deletions frontend/src/external/bcanSatchel/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ const initialState: AppState = {
yearFilter: [],
activeUsers: [],
inactiveUsers: [],
notifications: []

notifications: []
};

/**
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/main-page/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Dashboard from "./dashboard/Dashboard";
import GrantPage from "./grants/GrantPage";
import Header from "./header/Header";
import Users from "./users/Users";
import RestrictedPage from "./restricted/RestrictedPage";


function MainPage() {
Expand All @@ -16,6 +17,7 @@ function MainPage() {
<Route path="/all-grants" element={<GrantPage showOnlyMyGrants={false} />} />
<Route path="/my-grants" element={<GrantPage showOnlyMyGrants={true} />} />
<Route path="/users" element={<Users />} />
<Route path="/restricted" element={<RestrictedPage />} />
{/* fallback route */}
<Route path="*" element={<GrantPage />} />
</Routes>
Expand Down
72 changes: 42 additions & 30 deletions frontend/src/main-page/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import GanttYearGrantTimeline from "./Charts/GanttYearGrantTimeline";
import DonutMoneyApplied from "./Charts/DonutMoneyApplied";
import { ProcessGrantData } from "../grants/filter-bar/processGrantData";
import KPICards from "./Charts/KPICards";
import { Navigate } from "react-router-dom";
import { UserStatus } from "../../../../middle-layer/types/UserStatus";

const Dashboard = observer(() => {
// reset filters on initial render
Expand All @@ -28,7 +30,7 @@ const Dashboard = observer(() => {
updateStartDateFilter(null);
}, []);

const { yearFilter, allGrants } = getAppStore();
const { yearFilter, allGrants, user } = getAppStore();

const uniqueYears = Array.from(
new Set(
Expand All @@ -43,38 +45,48 @@ const Dashboard = observer(() => {

const { grants } = ProcessGrantData();

return (
<div className="dashboard-page px-12 py-4 mb-8 ">
<div className="flex flex-row justify-end gap-4 mb-6">
<CsvExportButton />
<DateFilter />
</div>

<div className=" gap-6 grid grid-cols-7">
<div className="col-span-3 h-full">
<KPICards
grants={grants}
recentYear={recentYear}
priorYear={priorYear}
/>
</div>
<div className="col-span-4">
<LineChartSuccessRate grants={grants} />
</div>
<div className="col-span-3">
<DonutMoneyApplied grants={grants} />
return user ? (
user?.position !== UserStatus.Inactive ? (
<div className="dashboard-page px-12 py-4 mb-8 ">
<div className="flex flex-row justify-end gap-4 mb-6">
<CsvExportButton />
<DateFilter />
</div>
<div className="col-span-4">
<StackedBarMoneyReceived grants={grants} />
</div>
<div className="col-span-5">
<GanttYearGrantTimeline recentYear={recentYear} grants={grants} uniqueYears={uniqueYears} />
</div>
<div className="col-span-2">
<BarYearGrantStatus recentYear={recentYear} grants={grants} />

<div className=" gap-6 grid grid-cols-7">
<div className="col-span-3 h-full">
<KPICards
grants={grants}
recentYear={recentYear}
priorYear={priorYear}
/>
</div>
<div className="col-span-4">
<LineChartSuccessRate grants={grants} />
</div>
<div className="col-span-3">
<DonutMoneyApplied grants={grants} />
</div>
<div className="col-span-4">
<StackedBarMoneyReceived grants={grants} />
</div>
<div className="col-span-5">
<GanttYearGrantTimeline
recentYear={recentYear}
grants={grants}
uniqueYears={uniqueYears}
/>
</div>
<div className="col-span-2">
<BarYearGrantStatus recentYear={recentYear} grants={grants} />
</div>
</div>
</div>
</div>
) : (
<Navigate to="restricted" replace />
)
) : (
<Navigate to="/login" replace />
);
});

Expand Down
75 changes: 42 additions & 33 deletions frontend/src/main-page/grants/GrantPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,73 @@ import { useEffect, useState } from "react";
import { Grant } from "../../../../middle-layer/types/Grant.ts";
import FilterBar from "./filter-bar/FilterBar.tsx";
import { useAuthContext } from "../../context/auth/authContext";
import { updateEndDateFilter, updateFilter, updateStartDateFilter, updateYearFilter } from "../../external/bcanSatchel/actions.ts";
import {
updateEndDateFilter,
updateFilter,
updateStartDateFilter,
updateYearFilter,
} from "../../external/bcanSatchel/actions.ts";
import { toJS } from "mobx";
import { UserStatus } from "../../../../middle-layer/types/UserStatus.ts";
import { Navigate } from "react-router-dom";

interface GrantPageProps {
showOnlyMyGrants?: boolean; //if true, filters grants by user email
}


function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) {
const [showNewGrantModal, setShowNewGrantModal] = useState(false);
const [selectedGrant, setSelectedGrant] = useState<Grant | null>(null);

const { user } = useAuthContext(); //gets current logged in user
const userObj = toJS(user);

const userObj = toJS(user);

const currentUserEmail = userObj?.email || ""; //safe fallback

console.log("Current logged-in user:", userObj);
// reset filters on initial render
useEffect(() => {
updateYearFilter([]);
updateFilter(null);
updateEndDateFilter(null);
updateStartDateFilter(null);
}, []);
updateYearFilter([]);
updateFilter(null);
updateEndDateFilter(null);
updateStartDateFilter(null);
}, []);

return (
<div className="grant-page px-8">
<div className="top-half">
</div>
return user ? (
user?.position !== UserStatus.Inactive ? (
<div className="grant-page px-8">
<div className="top-half"></div>
<div className="flex justify-end align-middle p-4 gap-4">
<GrantSearch />
<AddGrantButton onClick={() => setShowNewGrantModal(true)} />
</div>
<div className="grid grid-cols-5 gap-8 px-4">
<div className="col-span-1">
<FilterBar/>
</div>
<div className="bot-half col-span-4">
<div className="grant-list-container">
<GrantList
selectedGrantId={
selectedGrant ? selectedGrant.grantId : undefined
}
onClearSelectedGrant={() => setSelectedGrant(null)}
currentUserEmail={currentUserEmail}
showOnlyMyGrants={showOnlyMyGrants}
/>
<div className="grid grid-cols-5 gap-8 px-4">
<div className="col-span-1">
<FilterBar />
</div>
<div className="bot-half col-span-4">
<div className="grant-list-container">
<GrantList
selectedGrantId={
selectedGrant ? selectedGrant.grantId : undefined
}
onClearSelectedGrant={() => setSelectedGrant(null)}
currentUserEmail={currentUserEmail}
showOnlyMyGrants={showOnlyMyGrants}
/>
</div>
</div>
</div>
<div className="hidden-features">
{showNewGrantModal && (
<NewGrantModal onClose={() => setShowNewGrantModal(false)} />
)}
</div>
</div>
<div className="hidden-features">
{showNewGrantModal && (
<NewGrantModal onClose={() => setShowNewGrantModal(false)} />
)}
</div>
</div>
) :
<Navigate to="restricted" replace />
) : (
<Navigate to="/login" replace />
);
}

Expand Down
5 changes: 3 additions & 2 deletions frontend/src/main-page/header/UserButton.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { faUser } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link } from "react-router-dom";

const UserButton = () => {
return (
<div>
<a href="/main/users">
<Link to="users">
<button className="text-[#000000] focus:outline-none">
<FontAwesomeIcon icon={faUser} />
</button>
</a>
</Link>
</div>
);
};
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/main-page/restricted/RestrictedPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Link } from "react-router-dom";
import logo from "../../images/bcan_logo.svg";
import { ButtonColorOption } from "../../custom/RingButton";

function RestrictedPage() {
return (
<div className="flex justify-center gap-20 items-center h-[100vh] text-left">
<div>
<h1 className="text-7xl font-bold mb-5">So Sorry!</h1>
<p className="text-3xl font-bold mb-3">
You don't have access to this page.
</p>
<p className="text-xl mb-9">
Contact the admin if you think there's a mistake.
</p>
<Link to="/login">
<button
style={{
backgroundColor: ButtonColorOption.ORANGE ,
color: 'black',
borderStyle: 'solid', borderColor: 'black', borderWidth: '1px'
}}
className="py-2 px-4 rounded"
>
Back to Login
</button>
</Link>
</div>
<img
className="w-[400px] h-[400px] object-contain"
src={logo}
alt="BCAN logo"
/>
</div>
);
}

export default RestrictedPage;
Loading