Skip to content

Commit b534d17

Browse files
authored
chore: add menu Mentor (#288)
* first version * update check students * refactor: update UI text and layout for student progress features * remove duplicated CheckGitHubAccount
1 parent 7a34069 commit b534d17

4 files changed

Lines changed: 192 additions & 47 deletions

File tree

app/client/src/App.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,7 @@ function App() {
9393
<Route path="/" element={<Home />} />
9494
<Route path="/exercise/:id" element={<Workspace />} />
9595
<Route path="/end" element={<FinalScreen />} />
96-
<Route
97-
path="/check/:account"
98-
element={<CheckGitHubAccount />}
99-
/>{" "}
96+
<Route path="/check-student" element={<CheckGitHubAccount />} />
10097
<Route path="/graduates" element={<CheckGraduates />} />
10198
<Route
10299
path="/evaluate-students"

app/client/src/components/layout/BasicLayout.tsx

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { Link, Typography } from "@mui/material";
1+
import { Link, Typography, Button, Menu, MenuItem } from "@mui/material";
2+
import { School } from "@mui/icons-material";
23
import Box from "@mui/material/Box";
34
import { isMobileOnly } from "react-device-detect";
45
import { GitHubLoginButton } from "../github/GitHubLoginButton";
56
import { About } from "./About";
6-
import { useLocation } from "react-router-dom";
7+
import { useLocation, Link as RouterLink, useNavigate } from "react-router-dom";
8+
import { useState } from "react";
79

810
const NAV_HEIGHT = "50px";
911

@@ -13,6 +15,34 @@ interface IBasicLayoutProps {
1315
export const BasicLayout = ({ children }: IBasicLayoutProps) => {
1416
const location = useLocation();
1517
const path = location.pathname.substring(1);
18+
const navigate = useNavigate();
19+
20+
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
21+
22+
const menuOpen = Boolean(anchorEl);
23+
24+
const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
25+
setAnchorEl(event.currentTarget);
26+
};
27+
28+
const handleMenuClose = () => {
29+
setAnchorEl(null);
30+
};
31+
32+
const handleGraduatesClick = () => {
33+
navigate("/graduates");
34+
handleMenuClose();
35+
};
36+
37+
const handleStudentExercisesClick = () => {
38+
navigate("/check-student");
39+
handleMenuClose();
40+
};
41+
42+
const handleMentorUtilsClick = () => {
43+
navigate("/evaluate-students");
44+
handleMenuClose();
45+
};
1646
return (
1747
<Box
1848
className={path.replaceAll("/", "-") || "home"}
@@ -48,6 +78,45 @@ export const BasicLayout = ({ children }: IBasicLayoutProps) => {
4878
</Link>
4979
</Box>
5080
<Box sx={{ display: "flex", gap: 1, alignItems: "center", mr: 2 }}>
81+
<Button
82+
onClick={handleMenuClick}
83+
sx={{
84+
color: "#FFF",
85+
textTransform: "none",
86+
fontSize: 14,
87+
"&:hover": {
88+
backgroundColor: "rgba(255, 255, 255, 0.08)",
89+
},
90+
display: "flex",
91+
alignItems: "center",
92+
gap: 0.5,
93+
}}
94+
>
95+
<School sx={{ fontSize: 18 }} />
96+
Mentor
97+
</Button>
98+
<Menu
99+
anchorEl={anchorEl}
100+
open={menuOpen}
101+
onClose={handleMenuClose}
102+
MenuListProps={{
103+
"aria-labelledby": "admin-menu-button",
104+
}}
105+
sx={{
106+
"& .MuiPaper-root": {
107+
backgroundColor: "#333",
108+
color: "#FFF",
109+
},
110+
}}
111+
>
112+
<MenuItem onClick={handleGraduatesClick}>Graduates</MenuItem>
113+
<MenuItem onClick={handleStudentExercisesClick}>
114+
Student Progress
115+
</MenuItem>
116+
<MenuItem onClick={handleMentorUtilsClick}>
117+
Student Evaluation
118+
</MenuItem>
119+
</Menu>
51120
<About />
52121
{!isMobileOnly && <GitHubLoginButton />}
53122
</Box>
Lines changed: 118 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,141 @@
11
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
2-
import { List, ListItem, Typography } from "@mui/material";
2+
import { List, ListItem, Typography, TextField, Button } from "@mui/material";
33
import Box from "@mui/material/Box";
44
import { useParams } from "react-router-dom";
5+
import { useState } from "react";
56
import { useGetExercises } from "../../../queries/useGetExercises";
67
import { CircularProgressCenterLoader } from "../../shared/CircularProgressCenterLoader";
78

89
export const CheckGitHubAccount = () => {
910
const { account } = useParams();
10-
const { data: exercises, isLoading } = useGetExercises(account);
11+
const [accountInput, setAccountInput] = useState("");
12+
const [searchedAccount, setSearchedAccount] = useState<string | undefined>(account);
13+
const [hasSearched, setHasSearched] = useState(!!account);
14+
15+
const { data: exercises, isLoading } = useGetExercises(searchedAccount);
1116
const completedExercises =
1217
exercises?.filter((exercise) => exercise.completed)?.length ?? 0;
18+
19+
const handleSearch = () => {
20+
const trimmedAccount = accountInput.trim();
21+
if (trimmedAccount) {
22+
setSearchedAccount(trimmedAccount);
23+
setHasSearched(true);
24+
}
25+
};
26+
27+
const handleKeyPress = (e: React.KeyboardEvent) => {
28+
if (e.key === "Enter") {
29+
handleSearch();
30+
}
31+
};
1332
return (
1433
<Box
1534
sx={{
1635
display: "flex",
1736
justifyContent: "center",
1837
p: 6,
1938
flexDirection: "column",
39+
maxWidth: "800px",
40+
margin: "0 auto",
2041
}}
2142
>
22-
<Typography variant="h5">
23-
Results for GitHub account: {account}
24-
</Typography>{" "}
25-
{!isLoading && (
26-
<Typography>
27-
{completedExercises}/{exercises?.length ?? 54} exercises completed
28-
</Typography>
29-
)}
30-
<br />
31-
<Box sx={{ maxHeight: "calc(100vh - 300px)", overflowY: "scroll" }}>
32-
{isLoading ? (
33-
<CircularProgressCenterLoader />
34-
) : (
35-
<List>
36-
{exercises?.map((exercise) => (
37-
<ListItem
38-
sx={{
39-
my: 0,
40-
py: 0.5,
41-
}}
42-
key={exercise.id}
43-
>
44-
<Typography
45-
sx={{
46-
color: exercise.completed ? "#34b830" : "#999",
47-
}}
48-
>
49-
{exercise.name}
50-
</Typography>
51-
{exercise.completed && (
52-
<CheckCircleOutlineIcon
53-
sx={{ fontSize: 18, color: "#34b830", ml: 2 }}
54-
/>
55-
)}
56-
</ListItem>
57-
))}
58-
</List>
59-
)}
43+
<Typography variant="h4" sx={{ mb: 4, textAlign: "center" }}>
44+
Check Student Progress
45+
</Typography>
46+
47+
<Box sx={{ display: "flex", gap: 2, mb: 4, alignItems: "center" }}>
48+
<TextField
49+
label="GitHub Username"
50+
variant="outlined"
51+
fullWidth
52+
value={accountInput}
53+
onChange={(e) => setAccountInput(e.target.value)}
54+
onKeyPress={handleKeyPress}
55+
placeholder="e.g.: username"
56+
sx={{
57+
"& .MuiOutlinedInput-root": {
58+
color: "#FFF",
59+
"& fieldset": {
60+
borderColor: "#666",
61+
},
62+
"&:hover fieldset": {
63+
borderColor: "#999",
64+
},
65+
"&.Mui-focused fieldset": {
66+
borderColor: "#dd3d3d",
67+
},
68+
},
69+
"& .MuiInputLabel-root": {
70+
color: "#CCC",
71+
"&.Mui-focused": {
72+
color: "#dd3d3d",
73+
},
74+
},
75+
}}
76+
/>
77+
<Button
78+
variant="contained"
79+
onClick={handleSearch}
80+
disabled={!accountInput.trim()}
81+
sx={{
82+
backgroundColor: "#dd3d3d",
83+
"&:hover": {
84+
backgroundColor: "#bb2d2d",
85+
},
86+
"&:disabled": {
87+
backgroundColor: "#666",
88+
},
89+
minWidth: "120px",
90+
height: "56px",
91+
}}
92+
>
93+
Search
94+
</Button>
6095
</Box>
96+
97+
{hasSearched && searchedAccount && (
98+
<>
99+
<Typography variant="h5" sx={{ mb: 2 }}>
100+
Results for: {searchedAccount}
101+
</Typography>
102+
{!isLoading && (
103+
<Typography sx={{ mb: 3 }}>
104+
{completedExercises}/{exercises?.length ?? 54} exercises completed
105+
</Typography>
106+
)}
107+
<Box sx={{ maxHeight: "calc(100vh - 400px)", overflowY: "auto" }}>
108+
{isLoading ? (
109+
<CircularProgressCenterLoader />
110+
) : (
111+
<List>
112+
{exercises?.map((exercise) => (
113+
<ListItem
114+
sx={{
115+
my: 0,
116+
py: 0.5,
117+
}}
118+
key={exercise.id}
119+
>
120+
<Typography
121+
sx={{
122+
color: exercise.completed ? "#34b830" : "#999",
123+
}}
124+
>
125+
{exercise.name}
126+
</Typography>
127+
{exercise.completed && (
128+
<CheckCircleOutlineIcon
129+
sx={{ fontSize: 18, color: "#34b830", ml: 2 }}
130+
/>
131+
)}
132+
</ListItem>
133+
))}
134+
</List>
135+
)}
136+
</Box>
137+
</>
138+
)}
61139
</Box>
62140
);
63141
};

app/client/src/queries/useGetExercises.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { getUser } from "../utils/getUser";
77

88
export const useGetExercises = (user = getUser()) => {
99
return useQuery<IExercise[]>({
10-
queryKey: ["exercises"],
10+
queryKey: ["exercises", user],
1111
queryFn: async () => {
1212
const { data: exercises } = await axios.get(API_URL + "/exercises");
1313
const { data: completedExercises } = await axios.get(
@@ -25,5 +25,6 @@ export const useGetExercises = (user = getUser()) => {
2525
};
2626
});
2727
},
28+
enabled: !!user, // Only run the query if user is provided
2829
});
2930
};

0 commit comments

Comments
 (0)