Skip to content

Commit 288c413

Browse files
AnanthKini1shayanhalderIsaacNguyenlawwzhou
authored
54 account create/edit form (#63)
* feat: created form with status, program name, launch date, region, students, instruments, and language input * feat: added test route for creating program form * feat: functionality to add program directors and curriculum links, fixed state update on DOM * chore: clean up comments and remove test route * chore: add close and submit button with proper spacing * fix: moved test route above root and catch all routes * fix: removes localhost call and uses backend context instead to fetch program directors * fix: removed dummy data in curriculum links * feat: adds route to get program director names (joins with gcf_user table) * fix: corrects column name in select statement for program director name route * fix: added table name in order by statement * fix: updated programDirectors type as list of director objects, now displayed by first and last name * fix: removed colons in placeholders * fix: prevents selection of duplicate program directors * fix: fetches region data from db instead of hardcoded regions * docs: Added TODO in front of comments * fix: turns instruments into a dropdown with data from database * fix: enforced date and number input for date and # of students * fix: changed file from tsx to jsx, removed type annotations * fix: removes duplicates from instrument dropdown * initial component structure * Got Authcontext working * Basic autofill working * feat: updates to use drawer component * feat: adds dropdown for user type and autopopulates programs for program directors * feat: autopopulates role type and hides program field for non-program directors * feat: skeleton handlesubmit method for save logic * feat: adds create user logic * feat: uses firebase admin sdk to create new users without auto login issue * fix: updated create user connected to new backend route * adds new test routes * fix: deleted duplicate create user post request * feat: update existing user * feat: added autofill for email * feat: removes selected program tag and adds conditional rendering placeholder for password field * chore: removes console.log statements * Update lock files after dependency reinstall * fix: deleted button from program form * fix: deletes duplicate routes * fix: connects accountform to account.jsx by passing in targetUser as prop, deletes redundant if statements/useEffects, implements authContext for current user * feat: adds onSave prop to accountform to call every time user is updated/created, so account table automatically shows displayed changes by separating fetchData from useEffect * feat: fetch email in table first * feat: region assignment flow * feat: restrict regional director account creation --------- Co-authored-by: Shayan Halder <shayanhalder2389@gmail.com> Co-authored-by: IsaacNguyen <isaachugh33@gmail.com> Co-authored-by: Lawrence Zhou <zhoulh@uci.edu> Co-authored-by: Isaac Nguyen <isaachn@uci.edu>
1 parent b64e4c2 commit 288c413

10 files changed

Lines changed: 3068 additions & 3012 deletions

File tree

client/src/App.tsx

Lines changed: 13 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Admin } from "@/components/admin/Admin";
22
import { CatchAll } from "@/components/CatchAll";
33
import { Login } from "@/components/login/Login";
44
import { ProtectedRoute } from "@/components/ProtectedRoute";
5-
import Dashboard from "@/components/dashboard/Dashboard";
5+
import Dashboard from "@/components/dashboard/Dashboard.jsx";
66
import { Signup } from "@/components/signup/Signup";
77
import { UpdatesPage } from "@/components/updates/UpdatesPage";
88
import { AuthProvider } from "@/contexts/AuthContext";
@@ -28,62 +28,19 @@ const App = () => {
2828
<RoleProvider>
2929
<Router>
3030
<Routes>
31-
<Route
32-
path="/login"
33-
element={<Login />}
34-
/>
35-
<Route
36-
path="/signup"
37-
element={<Signup />}
38-
/>
39-
40-
<Route
41-
path="/"
42-
element={<ProtectedRoute element={<Layout />}/>}
43-
>
44-
45-
<Route
46-
path="admin"
47-
element={
48-
<ProtectedRoute
49-
element={<Admin />}
50-
allowedRoles={["admin"]} />}
51-
/>
52-
53-
{/* TODO(login): Change /account & /media route to protected when auth flow finalized; consider dropping :userId and using AuthContext (currentUser.uid) for current user. */}
54-
<Route
55-
path="account/:userId"
56-
element={<Account />}
57-
/>
58-
59-
<Route
60-
path="media/:userId"
61-
element={<Media />}
62-
/>
63-
64-
<Route
65-
path="dashboard/:userId"
66-
element={<Dashboard />}
67-
/>
68-
69-
70-
<Route
71-
path="create-program-form-test"
72-
element={<ProgramForm />}
73-
/>
74-
75-
{/* TODO(login): Consider route without :userId; use AuthContext (currentUser.uid) in UpdatesPage. */}
76-
<Route
77-
path="/updates/:userId"
78-
element={<UpdatesPage />}
79-
/>
80-
31+
<Route path="/login" element={<Login />} />
32+
<Route path="/signup" element={<Signup />} />
33+
34+
<Route path="/" element={<ProtectedRoute element={<Layout />}/>}>
35+
<Route path="admin" element={<ProtectedRoute element={<Admin />} allowedRoles={["admin"]} />} />
36+
<Route path="account/:userId" element={<Account />} />
37+
<Route path="media/:userId" element={<Media />} />
38+
<Route path="dashboard/:userId" element={<Dashboard />} />
39+
<Route path="create-program-form-test" element={<ProgramForm />} />
40+
<Route path="updates/:userId" element={<UpdatesPage />} />
8141
</Route>
82-
<Route
83-
path="*"
84-
element={<ProtectedRoute element={<CatchAll />} />}
85-
/>
8642

43+
<Route path="*" element={<ProtectedRoute element={<CatchAll />} />} />
8744
</Routes>
8845
</Router>
8946
</RoleProvider>
@@ -93,4 +50,4 @@ const App = () => {
9350
);
9451
};
9552

96-
export default App;
53+
export default App;
Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from "react";
1+
import { useCallback, useEffect, useState } from "react";
22

33
import { Box, Center, Flex, Heading, Spinner } from "@chakra-ui/react";
44

@@ -8,6 +8,7 @@ import { useParams } from "react-router-dom";
88

99
import { AccountsTable } from "./AccountsTable";
1010
import { AccountToolbar } from "./AccountToolbar";
11+
import { AccountForm } from "./AccountForm";
1112

1213
const getAccountsRoute = (role, userId) => {
1314
if (!userId) return null;
@@ -17,6 +18,7 @@ const getAccountsRoute = (role, userId) => {
1718
: `/gcf-users/${userId}/accounts`;
1819
};
1920

21+
2022
export const Account = () => {
2123
// TODO(login): Replace useParams userId with AuthContext (currentUser?.uid) when auth flow is finalized.
2224
const { userId } = useParams();
@@ -25,46 +27,43 @@ export const Account = () => {
2527
const [originalUsers, setOriginalUsers] = useState([]);
2628
const [isLoading, setIsLoading] = useState(true);
2729
const [searchQuery, setSearchQuery] = useState("");
30+
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
31+
const [selectedUser, setSelectedUser] = useState(null);
2832

2933
const { backend } = useBackendContext();
30-
// TODO: potentially create toggleable view for ALL accounts vs. only accounts created by the current user
31-
useEffect(() => {
32-
const fetchData = async () => {
33-
setIsLoading(true);
34-
35-
const route = getAccountsRoute(role, userId);
36-
37-
if (!route) {
38-
console.error("No valid route for accounts. Missing userId or role.");
39-
setIsLoading(false);
40-
return;
41-
}
42-
43-
try {
44-
const response = await backend.get(route);
4534

46-
// TODO: Update email and password fields when data is available
47-
const fetchedData = (response.data || []).map((item) => ({
48-
id: item.id,
49-
firstName: item.firstName,
50-
lastName: item.lastName,
51-
role: item.role,
52-
programs: Array.isArray(item.programs) ? item.programs : [],
53-
email: item.email ?? "-",
54-
password: "-",
55-
}));
56-
57-
setUsers(fetchedData);
58-
setOriginalUsers(fetchedData);
59-
} catch (error) {
60-
console.error("Error loading data:", error);
61-
} finally {
62-
setIsLoading(false);
63-
}
64-
};
35+
const fetchData = useCallback(async () => {
36+
setIsLoading(true);
37+
const route = getAccountsRoute(role, userId);
38+
if (!route) {
39+
console.error("No valid route for accounts. Missing userId or role.");
40+
setIsLoading(false);
41+
return;
42+
}
43+
try {
44+
const response = await backend.get(route);
45+
const fetchedData = (response.data || []).map((item) => ({
46+
id: item.id,
47+
firstName: item.firstName,
48+
lastName: item.lastName,
49+
role: item.role,
50+
programs: Array.isArray(item.programs) ? item.programs : [],
51+
email: item.email ?? "-",
52+
password: "-",
53+
}));
54+
55+
setUsers(fetchedData);
56+
setOriginalUsers(fetchedData);
57+
} catch (error) {
58+
console.error("Error loading data:", error);
59+
} finally {
60+
setIsLoading(false);
61+
}
62+
}, [backend, role, userId]);
6563

64+
useEffect(() => {
6665
fetchData();
67-
}, [backend, role, userId]);
66+
}, [fetchData]);
6867

6968
return (
7069
<Box
@@ -86,7 +85,10 @@ export const Account = () => {
8685
Accounts
8786
</Heading>
8887

89-
<AccountToolbar searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
88+
<AccountToolbar searchQuery={searchQuery} setSearchQuery={setSearchQuery} onNew = {() => {
89+
setIsDrawerOpen(true);
90+
setSelectedUser(null)
91+
}}/>
9092
</Flex>
9193

9294
{isLoading ? (
@@ -102,8 +104,13 @@ export const Account = () => {
102104
setData={setUsers}
103105
originalData={originalUsers}
104106
searchQuery={searchQuery}
107+
onUpdate = {(user) => {
108+
setSelectedUser(user)
109+
setIsDrawerOpen(true)
110+
}}
105111
/>
106112
)}
113+
<AccountForm targetUser = {selectedUser} isOpen = { isDrawerOpen } onClose = {() => setIsDrawerOpen(false)} onSave = {() => fetchData()}></AccountForm>
107114
</Box>
108115
);
109116
};

0 commit comments

Comments
 (0)