diff --git a/backend/src/grant/types/grant.types.ts b/backend/src/grant/types/grant.types.ts
index 2d090a29..a50cae4f 100644
--- a/backend/src/grant/types/grant.types.ts
+++ b/backend/src/grant/types/grant.types.ts
@@ -22,6 +22,9 @@ export class GrantResponseDto {
@ApiProperty({ description: 'When grant submission is due', example: '2024-06-01T00:00:00.000Z' })
application_deadline!: string;
+
+ @ApiProperty({ description: 'When grant was submitted', example: '2024-06-01T00:00:00.000Z' })
+ application_date?: string;
@ApiProperty({ description: 'Multiple report dates', type: [String], required: false })
report_deadlines?: string[];
@@ -75,6 +78,9 @@ export class AddGrantBody {
@ApiProperty({ description: 'When grant submission is due', example: '2024-06-01T00:00:00.000Z' })
application_deadline!: string;
+
+ @ApiProperty({ description: 'When grant was submitted', example: '2024-06-01T00:00:00.000Z' })
+ application_date?: string;
@ApiProperty({ description: 'Multiple report dates', type: [String], required: false, example: ['2024-12-01T00:00:00.000Z'] })
report_deadlines?: string[];
@@ -122,6 +128,9 @@ export class UpdateGrantBody {
@ApiProperty({ description: 'When grant submission is due', required: false })
application_deadline?: string;
+
+ @ApiProperty({ description: 'When grant was submitted', required: false })
+ application_date?: string;
@ApiProperty({ description: 'Multiple report dates', type: [String], required: false })
report_deadlines?: string[];
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index e3fada70..c34185eb 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -45,7 +45,6 @@
"@types/react-transition-group": "^4.4.12",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
- "baseline-browser-mapping": "^2.8.29",
"eslint": "^9.9.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
@@ -6226,12 +6225,15 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.8.29",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz",
- "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==",
+ "version": "2.10.8",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz",
+ "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==",
"license": "Apache-2.0",
"bin": {
- "baseline-browser-mapping": "dist/cli.js"
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
"node_modules/binary-extensions": {
@@ -6515,9 +6517,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001743",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz",
- "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==",
+ "version": "1.0.30001779",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz",
+ "integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==",
"funding": [
{
"type": "opencollective",
diff --git a/frontend/package.json b/frontend/package.json
index c0e5558b..4f28f545 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -48,7 +48,6 @@
"@types/react-transition-group": "^4.4.12",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
- "baseline-browser-mapping": "^2.8.29",
"eslint": "^9.9.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
diff --git a/frontend/src/Footer.tsx b/frontend/src/Footer.tsx
index ad2aa5d9..34927a24 100644
--- a/frontend/src/Footer.tsx
+++ b/frontend/src/Footer.tsx
@@ -4,7 +4,7 @@ import { FooterText } from "./translations/general";
const Footer: React.FC = () => {
return (
-
+
{FooterText.C4C_Motto}
diff --git a/frontend/src/custom/ActionConfirmation.tsx b/frontend/src/components/ActionConfirmation.tsx
similarity index 91%
rename from frontend/src/custom/ActionConfirmation.tsx
rename to frontend/src/components/ActionConfirmation.tsx
index 34dafc21..fd066ea8 100644
--- a/frontend/src/custom/ActionConfirmation.tsx
+++ b/frontend/src/components/ActionConfirmation.tsx
@@ -27,7 +27,7 @@ import { IoIosWarning } from "react-icons/io";
onClick={onCloseDelete}
>
e.stopPropagation()}
>
@@ -53,7 +53,7 @@ import { IoIosWarning } from "react-icons/io";
Warning
-
+
{warningMessage}
diff --git a/frontend/src/components/Button.tsx b/frontend/src/components/Button.tsx
index f6c3e0fa..60f4c8ee 100644
--- a/frontend/src/components/Button.tsx
+++ b/frontend/src/components/Button.tsx
@@ -2,39 +2,58 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
type ButtonProps = {
- text: string;
- onClick: () => void;
- className?: string;
- logo?: IconProp;
- logoPosition?: 'left' | 'right';
- disabled?: boolean;
- type?: "button" | "submit" | "reset";
-}
-
+ text: string;
+ onClick: () => void;
+ className?: string;
+ logo?: IconProp;
+ logoPosition?: "left" | "right" | "center";
+ disabled?: boolean;
+ type?: "button" | "submit" | "reset";
+};
// Button component where you can pass in text, onClick handler, optional className
// for styling, and an optional logo with its position.
//Styling is default, but can be overridden by passing in a className prop
-export default function Button({ text, onClick, className, logo, logoPosition, disabled, type }: ButtonProps) {
+export default function Button({
+ text,
+ onClick,
+ className,
+ logo,
+ logoPosition,
+ disabled,
+ type,
+}: ButtonProps) {
return (
-
);
-}
\ No newline at end of file
+}
diff --git a/frontend/src/components/SearchBar.tsx b/frontend/src/components/SearchBar.tsx
index debac718..5e7eb79e 100644
--- a/frontend/src/components/SearchBar.tsx
+++ b/frontend/src/components/SearchBar.tsx
@@ -27,7 +27,7 @@ export default function SearchBar({
/>
{
const store = getAppStore();
store.activeUsers = actionMessage.users;
persistToSessionStorage();
});
-/**
+/**
* setInactiveUsers mutator
-*/
+ */
mutator(setInactiveUsers, (actionMessage) => {
const store = getAppStore();
store.inactiveUsers = actionMessage.users;
@@ -42,13 +43,13 @@ mutator(setInactiveUsers, (actionMessage) => {
* setAuthState mutator
*/
mutator(setAuthState, (actionMessage) => {
- console.log('=== setAuthState MUTATOR CALLED ===');
+ console.log("=== setAuthState MUTATOR CALLED ===");
const store = getAppStore();
- console.log('Setting user:', actionMessage.user);
+ console.log("Setting user:", actionMessage.user);
store.isAuthenticated = actionMessage.isAuthenticated;
store.user = actionMessage.user;
store.accessToken = actionMessage.accessToken;
- console.log('Calling persistToSessionStorage...');
+ console.log("Calling persistToSessionStorage...");
persistToSessionStorage();
});
@@ -99,9 +100,23 @@ mutator(logoutUser, () => {
store.isAuthenticated = false;
store.user = null;
store.accessToken = null;
- sessionStorage.removeItem('bcanAppStore');
+ sessionStorage.removeItem("bcanAppStore");
});
+// Clears all store filters
+mutator(clearAllFilters, () => {
+ const store = getAppStore();
+ store.filterStatus = null;
+ store.startDateFilter = null;
+ store.endDateFilter = null;
+ store.searchQuery = "";
+ store.yearFilter = [];
+ store.userQuery = "";
+ store.emailFilter = false;
+ store.eligibleOnly = false;
+ store.amountMinFilter = null;
+ store.amountMaxFilter = null;
+});
/**
* Reassigns all grants to new grants from the backend.
@@ -117,16 +132,32 @@ mutator(fetchAllGrants, (actionMessage) => {
mutator(updateFilter, (actionMessage) => {
const store = getAppStore();
store.filterStatus = actionMessage.status;
-})
+});
mutator(updateStartDateFilter, (actionMessage) => {
const store = getAppStore();
store.startDateFilter = actionMessage.startDateFilter;
-})
+});
mutator(updateEndDateFilter, (actionMessage) => {
const store = getAppStore();
store.endDateFilter = actionMessage.endDateFilter;
+});
+
+mutator(updateUserEmailFilter, (actionMessage) => {
+ const store = getAppStore();
+ store.emailFilter = actionMessage.userEmailFilter;
+})
+
+mutator(updateEligibleOnly, (actionMessage) => {
+ const store = getAppStore();
+ store.eligibleOnly = actionMessage.eligibleOnly;
+})
+
+mutator(updateAmountRange, (actionMessage) => {
+ const store = getAppStore();
+ store.amountMinFilter = actionMessage.amountMinFilter;
+ store.amountMaxFilter = actionMessage.amountMaxFilter;
})
mutator(updateUserEmailFilter, (actionMessage) => {
@@ -148,33 +179,32 @@ mutator(updateAmountRange, (actionMessage) => {
mutator(updateSearchQuery, (actionMessage) => {
const store = getAppStore();
store.searchQuery = actionMessage.searchQuery;
-})
+});
mutator(updateYearFilter, (actionMessage) => {
const store = getAppStore();
store.yearFilter = actionMessage.yearFilter;
-})
+});
mutator(setNotifications, (actionMessage) => {
const store = getAppStore();
store.notifications = actionMessage.notifications;
-})
+});
mutator(updateSort, (actionMessage) => {
const store = getAppStore();
store.sort = actionMessage.sort;
-})
+});
mutator(updateUserQuery, (actionMessage) => {
const store = getAppStore();
store.userQuery = actionMessage.userQuery;
- console.log('Updated userQuery:', store.userQuery);
-})
+});
mutator(updateUserSort, (actionMessage) => {
const store = getAppStore();
store.userSort = actionMessage.sort;
-})
+});
mutator(removeProfilePic, () => {
const store = getAppStore();
@@ -192,4 +222,4 @@ mutator(removeProfilePic, () => {
}
persistToSessionStorage();
-});
\ No newline at end of file
+});
diff --git a/frontend/src/main-page/MainPage.tsx b/frontend/src/main-page/MainPage.tsx
index 07f91eba..8149cb35 100644
--- a/frontend/src/main-page/MainPage.tsx
+++ b/frontend/src/main-page/MainPage.tsx
@@ -1,4 +1,4 @@
-import { Routes, Route } from "react-router-dom";
+import { Routes, Route, useLocation } from "react-router-dom";
import Dashboard from "./dashboard/Dashboard";
import GrantPage from "./grants/GrantPage";
import NavBar from "./navbar/NavBar";
@@ -12,8 +12,9 @@ import { UserStatus } from "../../../middle-layer/types/UserStatus";
import { observer } from "mobx-react-lite";
import { Navigate } from "react-router-dom";
import { getAppStore } from "../external/bcanSatchel/store";
-import BellButton from "./navbar/Bell";
-import { useState } from "react";
+import BellButton from "./notifications/Bell";
+import { useEffect, useState } from "react";
+import { clearAllFilters } from "../external/bcanSatchel/actions";
interface PositionGuardProps {
children: React.ReactNode;
@@ -48,13 +49,20 @@ const PositionGuard = observer(
function MainPage() {
const [openModal, setOpenModal] = useState(false);
+ const location = useLocation();
+
+ // Clears all store filters when page changes
+ useEffect(() => {
+ clearAllFilters();
+ }, [location]);
+
return (
-
-
+
+
@@ -64,6 +72,7 @@ function MainPage() {
element={
+
}
/>
@@ -80,6 +89,7 @@ function MainPage() {
element={
+
}
/>
@@ -89,6 +99,7 @@ function MainPage() {
element={
+
}
/>
@@ -97,6 +108,7 @@ function MainPage() {
element={
+
}
/>
@@ -110,7 +122,6 @@ function MainPage() {
/>
-
);
diff --git a/frontend/src/main-page/dashboard/Dashboard.tsx b/frontend/src/main-page/dashboard/Dashboard.tsx
index dc490c83..30013291 100644
--- a/frontend/src/main-page/dashboard/Dashboard.tsx
+++ b/frontend/src/main-page/dashboard/Dashboard.tsx
@@ -1,34 +1,18 @@
-import CsvExportButton from "./CsvExportButton";
+import CsvExportButton from "./components/CsvExportButton";
-import DateFilter from "./DateFilter";
+import DateFilter from "./components/DateFilter";
import "./styles/Dashboard.css";
import { observer } from "mobx-react-lite";
-import StackedBarMoneyReceived from "./Charts/StackedBarMoneyReceived";
-import { useEffect } from "react";
-import {
- updateYearFilter,
- updateFilter,
- updateEndDateFilter,
- updateStartDateFilter,
- updateSearchQuery,
-} from "../../external/bcanSatchel/actions";
+import StackedBarMoneyReceived from "./components/StackedBarMoneyReceived";
import { getAppStore } from "../../external/bcanSatchel/store";
-import BarYearGrantStatus from "./Charts/BarYearGrantStatus";
-import LineChartSuccessRate from "./Charts/LineChartSuccessRate";
-import GanttYearGrantTimeline from "./Charts/GanttYearGrantTimeline";
-import DonutMoneyApplied from "./Charts/DonutMoneyApplied";
+import BarYearGrantStatus from "./components/BarYearGrantStatus";
+import LineChartSuccessRate from "./components/LineChartSuccessRate";
+import GanttYearGrantTimeline from "./components/GanttYearGrantTimeline";
+import DonutMoneyApplied from "./components/DonutMoneyApplied";
import { ProcessGrantData } from "../grants/filter-bar/processGrantData";
-import KPICards from "./Charts/KPICards";
+import KPICards from "./components/KPICards";
const Dashboard = observer(() => {
- // reset filters on initial render
- useEffect(() => {
- updateYearFilter([]);
- updateFilter(null);
- updateEndDateFilter(null);
- updateStartDateFilter(null);
- updateSearchQuery("");
- }, []);
const { yearFilter, allGrants } = getAppStore();
diff --git a/frontend/src/main-page/dashboard/Charts/BarYearGrantStatus.tsx b/frontend/src/main-page/dashboard/components/BarYearGrantStatus.tsx
similarity index 100%
rename from frontend/src/main-page/dashboard/Charts/BarYearGrantStatus.tsx
rename to frontend/src/main-page/dashboard/components/BarYearGrantStatus.tsx
diff --git a/frontend/src/main-page/dashboard/CsvExportButton.tsx b/frontend/src/main-page/dashboard/components/CsvExportButton.tsx
similarity index 88%
rename from frontend/src/main-page/dashboard/CsvExportButton.tsx
rename to frontend/src/main-page/dashboard/components/CsvExportButton.tsx
index 5df10059..2594fe51 100644
--- a/frontend/src/main-page/dashboard/CsvExportButton.tsx
+++ b/frontend/src/main-page/dashboard/components/CsvExportButton.tsx
@@ -1,13 +1,13 @@
import { useState } from "react";
-import { downloadCsv, CsvColumn } from "../../utils/csvUtils";
-import { Grant } from "../../../../middle-layer/types/Grant";
-import { ProcessGrantData } from "../../main-page/grants/filter-bar/processGrantData";
+import { downloadCsv, CsvColumn } from "../../../utils/csvUtils";
+import { Grant } from "../../../../../middle-layer/types/Grant";
+import { ProcessGrantData } from "../../grants/filter-bar/processGrantData";
import { observer } from "mobx-react-lite";
-import { getAppStore } from "../../external/bcanSatchel/store";
+import { getAppStore } from "../../../external/bcanSatchel/store";
import { faDownload } from "@fortawesome/free-solid-svg-icons";
-import Attachment from "../../../../middle-layer/types/Attachment";
-import POC from "../../../../middle-layer/types/POC";
-import Button from "../../components/Button";
+import Attachment from "../../../../../middle-layer/types/Attachment";
+import POC from "../../../../../middle-layer/types/POC";
+import Button from "../../../components/Button";
// Define the columns for the CSV export, including any necessary formatting.
const columns: CsvColumn
[] = [
{ key: "grantId", title: "Grant ID" },
diff --git a/frontend/src/main-page/dashboard/DateFilter.tsx b/frontend/src/main-page/dashboard/components/DateFilter.tsx
similarity index 90%
rename from frontend/src/main-page/dashboard/DateFilter.tsx
rename to frontend/src/main-page/dashboard/components/DateFilter.tsx
index 3317ce07..1ad4ea5b 100644
--- a/frontend/src/main-page/dashboard/DateFilter.tsx
+++ b/frontend/src/main-page/dashboard/components/DateFilter.tsx
@@ -1,10 +1,10 @@
import { useState, useEffect } from "react";
-import { updateYearFilter } from "../../external/bcanSatchel/actions";
-import { getAppStore } from "../../external/bcanSatchel/store";
+import { updateYearFilter } from "../../../external/bcanSatchel/actions";
+import { getAppStore } from "../../../external/bcanSatchel/store";
import { observer } from "mobx-react-lite";
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
import { faXmark } from "@fortawesome/free-solid-svg-icons";
-import Button from "../../components/Button";
+import Button from "../../../components/Button";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const DateFilter: React.FC = observer(() => {
@@ -52,7 +52,7 @@ const DateFilter: React.FC = observer(() => {
};
return (
-
+