Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
225e050
Adding user search
janekamata Feb 24, 2026
df67122
adding the row headers and rows
janekamata Feb 25, 2026
5b40647
Updating spacing and approve tab
janekamata Feb 25, 2026
1f148d5
Reorganizing components into folders
janekamata Feb 25, 2026
2d28693
Format and remove unused imports
janekamata Feb 25, 2026
50bed58
Action confirmation formatting
janekamata Feb 25, 2026
5a4a06b
Moving api calls into user actions file
janekamata Feb 25, 2026
740ecb5
First name sort
janekamata Feb 25, 2026
046f040
Search formatting
janekamata Feb 25, 2026
29ca4ac
Adding profile pic url
janekamata Feb 25, 2026
fba45de
Commenting out unused var
janekamata Feb 25, 2026
8745612
Button style change
janekamata Feb 25, 2026
ffae719
Commenting out pagination
janekamata Feb 25, 2026
1849210
Button active border
janekamata Feb 25, 2026
a85df7b
Fixed error where we weren't getting the user pfps
prooflesben Feb 25, 2026
f391fc5
Merge branch 'main' into 320-dev---implement-user-page-design
janekamata Feb 26, 2026
33a18eb
Updating guard format; adding notifications on all main page; updatin…
janekamata Feb 26, 2026
9d4c649
Removing profile pic from poc type
janekamata Feb 26, 2026
c872bfc
Abstracting search formatting
janekamata Feb 26, 2026
a9dadbf
Fixing action log
janekamata Feb 26, 2026
7b0b699
Navbar responsive style fix
janekamata Feb 26, 2026
980bd22
Rounded styling
janekamata Feb 26, 2026
f75f448
Adding inactive restricted page vs not admin
janekamata Feb 26, 2026
7b40fb3
Removing old users component
janekamata Feb 26, 2026
3b678c6
Fixing placeholder search value
janekamata Feb 26, 2026
bf6f4b6
notification popup border
janekamata Feb 26, 2026
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
14 changes: 2 additions & 12 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,12 +732,7 @@ async addUserToGroup(
this.logger.log('Executing DynamoDB scan with filter for Inactive users...');
const result = await this.dynamoDb.scan(params).promise();

const users: User[] = (result.Items || []).map((item) => ({
position: item.position as UserStatus,
email: item.email,
firstName: item.firstName,
lastName: item.lastName
}));
const users: User[] = (result.Items || []).map((item) => item as User);

this.logger.log(`✅ Successfully retrieved ${users.length} inactive users`);
return users;
Expand Down Expand Up @@ -787,12 +782,7 @@ async getAllActiveUsers(): Promise<User[]> {
this.logger.error("DynamoDB scan result:", result);
throw new NotFoundException("No active users found.");
}
const users: User[] = (result.Items || []).map((item) => ({
position: item.position as UserStatus,
email: item.email,
firstName: item.firstName,
lastName: item.lastName
}));
const users: User[] = (result.Items || []).map((item) => item as User);

this.logger.debug(`Fetched ${users.length} active users.`);

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function Button({ text, onClick, className, logo, logoPosition, d
return (
<button onClick={onClick} disabled={disabled || false} type={type || "button"}
className={`
px-4 py-2 rounded-3xl font-medium text-black border-2 active:bg-primary-900 active:text-white
px-4 py-2 rounded-3xl font-medium text-black border-2 active:bg-primary-900 active:border-primary-900 active:text-white
flex items-center justify-center transition-opacity
${className} ${disabled
? "cursor-not-allowed opacity-50"
Expand Down
42 changes: 42 additions & 0 deletions frontend/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { IoMdSearch } from "react-icons/io";

type SearchBarProps = {
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
userInput: string;
text: string;
};

export default function SearchBar({
handleInputChange,
userInput,
text,
}: SearchBarProps) {
return (
<div className="w-full relative">
{/* Absolutely-positioned icon */}
<IoMdSearch
style={{
position: "absolute",
top: "50%",
left: "0.7rem",
transform: "translateY(-50%)",
pointerEvents: "none",
zIndex: 2,
marginLeft: "2px",
}}
/>
<input
placeholder={`Search for a ${text}...`}
className="w-full px-4 py-2 rounded-3xl font-medium text-black border-2 flex items-center justify-center border-grey-500"
onChange={handleInputChange}
value={userInput}
style={{ paddingLeft: "2rem" }} // make room for the icon
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
}
}}
/>
</div>
);
}
19 changes: 5 additions & 14 deletions frontend/src/custom/ActionConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Button from "../components/Button";
import { IoIosWarning } from "react-icons/io";

{/* The popup that appears on delete */}
Expand All @@ -22,7 +23,7 @@ import { IoIosWarning } from "react-icons/io";

return (
<div
className=" fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 transition-opacity duration-300"
className=" fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[1500] transition-opacity duration-300"
onClick={onCloseDelete}
>
<div
Expand Down Expand Up @@ -63,21 +64,11 @@ import { IoIosWarning } from "react-icons/io";

{/* Buttons */}
<div className="flex w-full justify-between ">
<button
className="rounded-lg hover:bg-gray-200 border-2 border-black bg-red-light transition-colors w-32 h-12"
onClick={onCloseDelete}
>
No, cancel
</button>
<button
className="rounded-lg text-black border-2 border-black hover:bg-red-700 transition-colors w-32 h-12"
onClick={() => {
<Button text="No, cancel" onClick={onCloseDelete} className=" text-red border-red hover:border-red hover:bg-red-light active:bg-red" />
<Button text="Yes, confirm" onClick={() => {
onConfirmDelete();
onCloseDelete();
}}
>
Yes, confirm
</button>
}} className="border-grey-500" />
</div>

</div>
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/external/bcanSatchel/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export const updateSort = action(
(sort: {header: keyof Grant, asc: boolean}) => ({ sort, })
);

export const updateUserSort = action(
"updateUserSort",
(sort: {header: keyof User, sort: "asc" | "desc" | "none"}) => ({ sort, })
);

/**
* Append a new grant to the current list of grants.
*/
Expand All @@ -75,6 +80,11 @@ export const updateSearchQuery = action(
(searchQuery: string) => ({ searchQuery })
);

export const updateUserQuery = action(
"updateUserQuery",
(userQuery: string) => ({ userQuery })
);

export const setNotifications = action(
'setNotifications',
(notifications: Notification[]) => ({notifications})
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/external/bcanSatchel/mutators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
updateSearchQuery,
updateYearFilter,
setNotifications,
updateSort
updateSort,
updateUserQuery,
updateUserSort,
} from './actions';
import { getAppStore, persistToSessionStorage } from './store';
import { setActiveUsers, setInactiveUsers } from './actions';
Expand Down Expand Up @@ -117,3 +119,14 @@ 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;
})
4 changes: 4 additions & 0 deletions frontend/src/external/bcanSatchel/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export interface AppState {
activeUsers: User[] | [];
inactiveUsers: User[] | [];
sort: {header: keyof Grant, asc: boolean} | null;
userSort: {header: keyof User, sort: "asc" | "desc" | "none"} | null;
notifications: Notification[];
userQuery: string;
}

// Define initial state
Expand All @@ -36,6 +38,8 @@ const initialState: AppState = {
inactiveUsers: [],
notifications: [],
sort: null,
userSort: null,
userQuery: '',
};

/**
Expand Down
103 changes: 94 additions & 9 deletions frontend/src/main-page/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,120 @@ import { Routes, Route } from "react-router-dom";
import Dashboard from "./dashboard/Dashboard";
import GrantPage from "./grants/GrantPage";
import NavBar from "./navbar/NavBar";
import Users from "./users/Users";
import RestrictedPage from "./restricted/RestrictedPage";
import CashFlowPage from "./cash-flow/CashFlowPage";
import Settings from "./settings/Settings";
import Footer from "../Footer";
import UsersPage from "./users/UsersPage";

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";

interface PositionGuardProps {
children: React.ReactNode;
adminOnly?: boolean;
}

const PositionGuard = observer(
({ children, adminOnly = false }: PositionGuardProps) => {
const { user } = getAppStore();

// If user hasn't been resolved yet, avoid redirect
if (user === undefined) {
return null; // or a loading spinner
}

if (!user) {
return <Navigate to="/login" replace />;
}

if (
user.position === undefined ||
user.position === UserStatus.Inactive ||
(user.position === UserStatus.Employee && adminOnly)
) {
return <Navigate to="/restricted" replace />;
}

return <>{children}</>;
},
);

function MainPage() {
const [openModal, setOpenModal] = useState(false);

return (
<div className="w-full flex-row flex h-screen overflow-hiden">
<div>
<NavBar />
</div>
<div className="px-6 lg:px-10 py-8 pt-12 w-full h-screen overflow-y-auto">
<div className="px-6 lg:px-10 py-8 pt-8 w-full h-screen overflow-y-auto">
<div className="min-h-screen mb-8">
<div className="bell-container flex justify-end w-full">
<BellButton setOpenModal={setOpenModal} openModal={openModal} />
</div>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route
path="/dashboard"
element={
<PositionGuard adminOnly={false}>
<Dashboard />
</PositionGuard>
}
/>
<Route
path="/all-grants"
element={<GrantPage showOnlyMyGrants={false} />}
element={
<PositionGuard adminOnly={false}>
<GrantPage showOnlyMyGrants={false} />
</PositionGuard>
}
/>
<Route
path="/my-grants"
element={<GrantPage showOnlyMyGrants={true} />}
element={
<PositionGuard adminOnly={false}>
<GrantPage showOnlyMyGrants={true} />
</PositionGuard>
}
/>
<Route
path="/users"
element={
<PositionGuard adminOnly={true}>
<UsersPage />
</PositionGuard>
}
/>
<Route path="/users" element={<Users />} />
<Route path="/restricted" element={<RestrictedPage />} />
<Route path="/cash-flow" element={<CashFlowPage />} />
<Route path="/settings" element={<Settings />} />
<Route path="*" element={<GrantPage />} />
<Route
path="/cash-flow"
element={
<PositionGuard adminOnly={false}>
<CashFlowPage />
</PositionGuard>
}
/>
<Route
path="/settings"
element={
<PositionGuard adminOnly={false}>
<Settings />
</PositionGuard>
}
/>
<Route
path="*"
element={
<PositionGuard adminOnly={false}>
<GrantPage showOnlyMyGrants={false} />
</PositionGuard>
}
/>
</Routes>
</div>
<Footer />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const DonutMoneyApplied = observer(({ grants }: { grants: Grant[] }) => {
},
)}M`}
</div>
<div className={`gap-1 flex flex-col absolute ${width > 250 ? "mt-12" : "mt-2"}`}>
<div className={`gap-1 flex break-all flex-col absolute ${width > 250 ? "mt-12" : "mt-2"}`}>
<LabelItem
name="Received"
percent={sumReceived / total}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/main-page/dashboard/DateFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const DateFilter: React.FC = observer(() => {
className="bg-white border-grey-500 inline-flex items-center justify-between text-sm lg:text-base"
/>
<div
className={`z-[100] absolute top-10 w-64 lg:w-80 bg-white ${showDropdown ? "" : "hidden"} rounded-md border-2 border-gray-200 shadow-lg`}
className={`z-[100] absolute top-10 w-64 lg:w-80 bg-white ${showDropdown ? "" : "hidden"} rounded-md border-2 border-grey-500 shadow-lg`}
>
<button
className="close-button absolute top-3 right-4 text-lg"
Expand Down Expand Up @@ -100,8 +100,8 @@ const DateFilter: React.FC = observer(() => {
</li>
))}
</ul>
<hr className="border-t mx-4 border-gray-200" />
<button className="p-2" onClick={() => handleReset()}>
<hr className="border-t mx-4 border-grey-400" />
<button className="p-2 border-0 hover:text-grey-600" onClick={() => handleReset()}>
Reset
</button>
</div>
Expand Down
Loading