Skip to content

Commit f759fe5

Browse files
committed
add normal users table
1 parent 6c48897 commit f759fe5

File tree

7 files changed

+204
-8
lines changed

7 files changed

+204
-8
lines changed

UserService/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import cors from "cors";
77
// import { Socket, Server } from 'socket.io';
88
// import { Server as ServerHttp } from 'http';
99

10-
import { handleCreateUser, handleDeleteUser, handleGetUser, handleUpdateUser, handleCreateAdminUser, handleGetAdminUsers } from "./user/user.controller";
10+
import { handleCreateUser, handleDeleteUser, handleGetUser, handleUpdateUser, handleCreateAdminUser, handleGetAdminUsers, handleGetNormalUsers } from "./user/user.controller";
1111
const app = express();
1212
const port = 3004;
1313
app.use(cors());
@@ -25,6 +25,7 @@ app.get("/", (req, res) => {
2525

2626
app.post("/user", handleCreateUser);
2727
app.get("/adminusers", handleGetAdminUsers);
28+
app.get("/normalusers", handleGetNormalUsers);
2829
app.post("/useradmin", handleCreateAdminUser);
2930
app.get("/user", handleGetUser);
3031
app.put("/user", handleUpdateUser);

UserService/src/user/user.controller.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Request, Response } from "express";
22

3-
import { createAdminUser, delUser, createUser, getAdminUsers, getUser, updateUser } from "./user.service";
3+
import { createAdminUser, delUser, createUser, getAdminUsers, getUser, updateUser, getNormalUsers } from "./user.service";
44

55
export async function handleCreateUser(req: Request, res: Response) {
66
try {
@@ -55,6 +55,17 @@ export async function handleGetAdminUsers(req: Request, res: Response) {
5555
}
5656
}
5757

58+
export async function handleGetNormalUsers(req: Request, res: Response) {
59+
try {
60+
console.log(`getting normal users`);
61+
const result = await getNormalUsers();
62+
res.status(200).send(result);
63+
} catch (error) {
64+
console.error(error);
65+
res.status(500).send(error);
66+
}
67+
}
68+
5869
export async function handleUpdateUser(req: Request, res: Response) {
5970
try {
6071
const email = req.body.params.email;

UserService/src/user/user.service.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,34 @@ export async function getAdminUsers(): Promise<User[]> {
109109
}
110110
}
111111

112+
export async function getNormalUsers(): Promise<User[]> {
113+
try {
114+
const q = query(collection(db, "users"), where("role", "==", "user"));
115+
const querySnapshot = await getDocs(q);
116+
let userArray: (User)[];
117+
userArray = []
118+
querySnapshot.forEach((doc) => {
119+
const user = doc.data()
120+
userArray = userArray.concat([{
121+
email: user.email,
122+
name: user.name ? user.name : undefined,
123+
year: user.year ? user.year : undefined,
124+
major: user.major ? user.major : undefined,
125+
role: user.role,
126+
completed: user.completed,
127+
}])
128+
})
129+
130+
if (querySnapshot) {
131+
return Promise.resolve(userArray);
132+
133+
}
134+
return Promise.reject("no such user");
135+
} catch (error) {
136+
return Promise.reject(error);
137+
}
138+
}
139+
112140
export async function updateUser(email: string, params: any): Promise<User> {
113141
try {
114142
const document = doc(db, "users", email);

frontend/src/api/user/data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export const createUser = (email: string) =>
66

77
export const getAllAdminUsers = () => UserHttpClient.get<UserModel[]>("/adminusers");
88

9+
export const getAllNormalUsers = () => UserHttpClient.get<UserModel[]>("/normalusers");
10+
911
export const createAdminUser = (email: string) =>
1012
UserHttpClient.post<UserModel>("/useradmin", { email: email });
1113

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Select, MenuItem, TablePagination, SelectChangeEvent, Grid, Typography, Button } from '@mui/material';
3+
import { useData } from '../data/data.context';
4+
import { updateUser } from '../api/user';
5+
6+
interface NormalUser {
7+
email: string;
8+
name?: string;
9+
year?: string;
10+
major?: string;
11+
role: string;
12+
completed: number;
13+
}
14+
15+
const ITEMS_PER_PAGE_OPTIONS = [5, 10]; // Number of items to display per page
16+
17+
const NormalUsersTable: React.FC = () => {
18+
const [normalUsersData, setNormalUsers] = useState<NormalUser[]>([]);
19+
const [currentPage, setCurrentPage] = useState<number>(0);
20+
const [itemsPerPage, setItemsPerPage] = useState<number>(ITEMS_PER_PAGE_OPTIONS[0]);
21+
const { normalUsers, getNormalUsers } = useData();
22+
23+
useEffect(() => {
24+
async function getUsers() {
25+
getNormalUsers();
26+
}
27+
getUsers();
28+
}, []);
29+
30+
useEffect(() => {
31+
setNormalUsers(normalUsers);
32+
console.log('check')
33+
}, [normalUsers]);
34+
35+
const handlePageChange = (event: unknown, newPage: number) => {
36+
setCurrentPage(newPage);
37+
};
38+
39+
const handleChangeItemsPerPage = (
40+
event: SelectChangeEvent<unknown>,
41+
) => {
42+
setItemsPerPage(event.target.value as number);
43+
setCurrentPage(0);
44+
};
45+
46+
const giveAdmin = async (user: NormalUser, index: number) => {
47+
await updateUser({
48+
email: user?.email ? user?.email : "",
49+
name: user?.name ? user?.name : "",
50+
year: user?.year ? user?.year : "",
51+
major: user?.major ? user.major : "",
52+
role: "admin",
53+
completed: user?.completed ? user.completed : 0,
54+
})
55+
setNormalUsers(normalUsersData.filter((e, i) => i !== index));
56+
}
57+
58+
const indexOfLastUser = (currentPage + 1) * itemsPerPage;
59+
const indexOfFirstUser = indexOfLastUser - itemsPerPage;
60+
const currentUsers = normalUsersData.slice(indexOfFirstUser, indexOfLastUser);
61+
62+
return (
63+
<><div style={{ maxHeight: '400px', overflowY: 'auto', width: '100%' }}>
64+
65+
<TableContainer component={Paper} style={{ margin: '10px', padding: '10px' }}>
66+
<Grid container>
67+
<Grid item xs={3}>
68+
<Typography fontWeight={600}>Normal Users:</Typography>
69+
</Grid>
70+
</Grid>
71+
<Table style={{ minWidth: 650, fontSize: '14px' }}>
72+
<TableHead>
73+
<TableRow >
74+
<TableCell>Email</TableCell>
75+
<TableCell>Name</TableCell>
76+
<TableCell>Year</TableCell>
77+
<TableCell>Major</TableCell>
78+
{/* <TableCell>Role</TableCell> */}
79+
{/* <TableCell>Completed</TableCell> */}
80+
<TableCell>Grant Privileges</TableCell>
81+
</TableRow>
82+
</TableHead>
83+
<TableBody>
84+
{currentUsers.map((user:NormalUser, index:number) => (
85+
<TableRow key={index}>
86+
<TableCell>{user.email}</TableCell>
87+
<TableCell>{user.name}</TableCell>
88+
<TableCell>{user.year}</TableCell>
89+
<TableCell>{user.major}</TableCell>
90+
<TableCell>
91+
<Button onClick={() => giveAdmin(user, index)}>
92+
Confirm
93+
</Button>
94+
</TableCell>
95+
96+
{/* <TableCell>{user.role}</TableCell> */}
97+
{/* <TableCell>{user.completed}</TableCell> */}
98+
</TableRow>
99+
))}
100+
</TableBody>
101+
</Table>
102+
</TableContainer>
103+
</div><div style={{ display: 'flex', alignItems: 'center' }}>
104+
<Select
105+
value={itemsPerPage}
106+
onChange={handleChangeItemsPerPage}
107+
style={{ marginTop: '10px' }}
108+
>
109+
{ITEMS_PER_PAGE_OPTIONS.map((option) => (
110+
<MenuItem key={option} value={option}>
111+
{`${option} per page`}
112+
</MenuItem>
113+
))}
114+
</Select>
115+
116+
<TablePagination
117+
rowsPerPageOptions={[]}
118+
component="div"
119+
count={normalUsersData.length}
120+
rowsPerPage={itemsPerPage}
121+
page={currentPage}
122+
onPageChange={handlePageChange} />
123+
</div></>
124+
125+
);
126+
};
127+
128+
export default NormalUsersTable;

frontend/src/data/data.context.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createContext, ReactNode, useContext, useMemo, useState } from "react";
22

33
import { getAllQuestions } from "../api/questions/data";
4-
import { getAllAdminUsers } from "../api/user";
4+
import { getAllAdminUsers, getAllNormalUsers } from "../api/user";
55

66
interface Response {
77
type: "success" | "error" | undefined;
@@ -20,7 +20,7 @@ interface Question {
2020
examples: Example[];
2121
}
2222

23-
interface AdminUser {
23+
interface User {
2424
email: string;
2525
name?: string;
2626
year?: string;
@@ -39,8 +39,10 @@ interface DataContextData {
3939
response: Response;
4040
questions: Question[];
4141
getQuestions: () => void;
42-
adminUsers: AdminUser[];
42+
adminUsers: User[];
4343
getAdminUsers: () => void;
44+
normalUsers: User[];
45+
getNormalUsers: () => void;
4446
// getExamples: (id: string) => void;
4547
}
4648

@@ -60,14 +62,17 @@ const DataContext = createContext<DataContextData>({
6062
getQuestions: () => undefined,
6163
adminUsers: [],
6264
getAdminUsers: () => undefined,
65+
normalUsers: [],
66+
getNormalUsers: () => undefined,
6367
// getExamples: (id: string) => undefined,
6468
});
6569

6670
export function DataContextProvider({ children }: DataContextProviderProps) {
6771
const [loading, setLoading] = useState(false);
6872
const [response, setResponse] = useState<Response>(emptyResponse);
6973
const [questions, setQuestions] = useState<Question[]>([]);
70-
const [adminUsers, setAdminUsers] = useState<AdminUser[]>([]);
74+
const [adminUsers, setAdminUsers] = useState<User[]>([]);
75+
const [normalUsers, setNormalUsers] = useState<User[]>([]);
7176

7277
const getQuestions = async () => {
7378
try {
@@ -109,10 +114,29 @@ export function DataContextProvider({ children }: DataContextProviderProps) {
109114
}
110115
};
111116

117+
const getNormalUsers = async () => {
118+
try {
119+
setLoading(true);
120+
const result = await (await getAllNormalUsers()).data;
121+
setLoading(false);
122+
setNormalUsers(result);
123+
setResponse({
124+
type: "success",
125+
message: "successfully retrieved normal users",
126+
});
127+
} catch (e) {
128+
setLoading(false);
129+
setResponse({
130+
type: "error",
131+
message: e,
132+
});
133+
}
134+
};
135+
112136
const dataContextProviderValue = useMemo(
113-
() => ({ loading, response, questions, getQuestions, adminUsers, getAdminUsers }),
137+
() => ({ loading, response, questions, getQuestions, adminUsers, getAdminUsers, normalUsers, getNormalUsers}),
114138
//eslint-disable-next-line react-hooks/exhaustive-deps
115-
[loading, response, questions, adminUsers]
139+
[loading, response, questions, adminUsers, normalUsers]
116140
);
117141

118142
return (

frontend/src/pages/profile.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Values } from "../components/DropDownOrTextField";
1717
import { updateUser } from "../api/user";
1818
import AdminUsersTable from "../components/AdminUsersTable";
1919
import DeleteButtonModal from "../components/UserService/DeleteButton";
20+
import NormalUsersTable from "../components/NormalUsersTable";
2021

2122
export default function Profile() {
2223
const { user, setUser } = useAuth();
@@ -82,6 +83,7 @@ export default function Profile() {
8283
</Grid>
8384
<DeleteButtonModal/>
8485
{user?.role == 'maintainer' && <AdminUsersTable/>}
86+
{user?.role == 'maintainer' && <NormalUsersTable/>}
8587
</Container>
8688
</Box>
8789
);

0 commit comments

Comments
 (0)