Skip to content

Commit 39898c0

Browse files
authored
feat: mentorships bird view (#819)
1 parent e3adf3a commit 39898c0

File tree

12 files changed

+199
-16
lines changed

12 files changed

+199
-16
lines changed

src/Me/AuthorizationRoute.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { FC } from 'react';
2+
import { Redirect, Route, RouteProps } from 'react-router-dom';
3+
import { useUser } from '../context/userContext/UserContext';
4+
import { UserRole } from '../types/models';
5+
6+
type AuthorizationRouteProps = RouteProps & {
7+
role: UserRole;
8+
};
9+
10+
export const AuthorizationRoute: FC<AuthorizationRouteProps> = ({
11+
role,
12+
children,
13+
}) => {
14+
const { currentUser } = useUser();
15+
16+
return (
17+
<Route
18+
render={() =>
19+
currentUser?.roles.includes(role) ? (
20+
<>{children}</>
21+
) : (
22+
<Redirect to="/" />
23+
)
24+
}
25+
/>
26+
);
27+
};

src/Me/Main.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,23 @@ const Main: FC = ({ children }) => {
1515
export default Main;
1616

1717
const Content = styled.div`
18-
margin-top: -50px;
18+
gap: 10px;
19+
display: flex;
20+
flex-wrap: wrap;
1921
padding: 0 16px;
22+
margin-top: -50px;
23+
justify-content: center;
2024
2125
@media ${desktop} {
22-
width: 400px;
26+
/* width: 400px; */
2327
margin-right: auto;
2428
margin-left: auto;
2529
padding-bottom: 10px;
2630
}
2731
2832
@media ${mobile} {
2933
padding-bottom: ${mobileNavHeight + 8}px;
34+
flex-direction: column;
35+
gap: 20px;
3036
}
3137
`;

src/Me/Me.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react';
12
import { Redirect, Route, Switch } from 'react-router-dom';
23
import type { RouteComponentProps } from 'react-router-dom';
34
import { ToastContainer } from 'react-toastify';
@@ -11,6 +12,10 @@ import Home from './Routes/Home';
1112
import MentorshipRequests from '../Me/MentorshipRequests';
1213
import { GlobalStyle } from './styles/global';
1314
import { desktop } from './styles/shared/devices';
15+
import { AuthorizationRoute } from './AuthorizationRoute';
16+
17+
18+
const Admin = React.lazy(() => import(/* webpackChunkName: "Admin" */ './Routes/Admin'));
1419

1520
type MeProps = RouteComponentProps & {};
1621

@@ -24,6 +29,10 @@ const meRoutes = [
2429
path: '/me/requests',
2530
name: 'Mentorships',
2631
},
32+
{
33+
path: '/me/admin',
34+
name: 'Admin',
35+
},
2736
];
2837

2938
//function to find the header title based on the path
@@ -48,6 +57,9 @@ const Me = ({
4857
<Route path={`${url}/requests`}>
4958
<MentorshipRequests />
5059
</Route>
60+
<AuthorizationRoute path={`${url}/admin`} role={'Admin'}>
61+
<Admin />
62+
</AuthorizationRoute>
5163
<Route path={`${url}`}>
5264
<Home />
5365
</Route>

src/Me/MentorshipRequests/UsersList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const renderList = ({
103103
value: status,
104104
theme: STATUS_THEME[status],
105105
}}
106-
info={formatRequestTime(Date.parse(date))}
106+
info={formatRequestTime(new Date(date))}
107107
>
108108
<ReqContent
109109
status={status}

src/Me/Navigation/Navbar.test.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import React from 'react';
21
import { BrowserRouter as Router } from 'react-router-dom';
32
import { render } from '@testing-library/react';
43
import Navbar from './Navbar';
4+
import { UserProvider } from '../../context/userContext/UserContext';
55

66
describe('Navbar', () => {
77
test('Navbar renders', () => {
88
const { getByText } = render(
9-
<Router>
10-
<Navbar />
11-
</Router>
9+
<UserProvider>
10+
<Router>
11+
<Navbar />
12+
</Router>
13+
</UserProvider>
1214
);
1315
expect(getByText('Home')).toBeTruthy();
1416
expect(getByText('Mentors')).toBeTruthy();

src/Me/Navigation/Navbar.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ReactComponent as IconHome } from '../../assets/me/home.svg';
99
import { ReactComponent as Mentorships } from '../../assets/me/icon-survey.svg';
1010
import { ReactComponent as IconMentors } from '../../assets/me/mentors.svg';
1111
import { ReactComponent as IconLogout } from '../../assets/me/icon-door-exit.svg';
12+
import { useUser } from '../../context/userContext/UserContext';
1213

1314
const MenuItem = ({
1415
icon: Icon,
@@ -32,6 +33,7 @@ const MenuItem = ({
3233
};
3334

3435
const Navbar = () => {
36+
const { isAdmin } = useUser();
3537
return (
3638
<>
3739
<Menu>
@@ -42,6 +44,9 @@ const Navbar = () => {
4244
<MenuItem to="/me" icon={IconHome} label="Home" />
4345
<MenuItem to="/me/requests" icon={Mentorships} label="Mentorships" />
4446
<MenuItem to="/" icon={IconMentors} label="Mentors" />
47+
{isAdmin && (
48+
<MenuItem to="/me/admin" icon={IconMentors} label="Admin" />
49+
)}
4550
<Logout to={window.location.pathname} onClick={auth.doLogout}>
4651
<IconLogout />
4752
<Label>{messages.LOGOUT}</Label>

src/Me/Routes/Admin.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { useEffect, useState } from 'react';
2+
import {
3+
getAllMentorshipRequests,
4+
sendStaledRequestEmail,
5+
} from '../../api/admin';
6+
import Card from '../components/Card';
7+
import { MentorshipRequest } from '../../types/models';
8+
import { formatRequestTime } from '../../helpers/mentorship';
9+
import Button from '../components/Button';
10+
import { toast } from 'react-toastify';
11+
12+
const Admin = () => {
13+
const [mentorshipLoading, setMentorshipLoading] = useState<string | null>();
14+
const [mentorshipRequests, setMentorshipRequests] = useState<
15+
MentorshipRequest[]
16+
>([]);
17+
18+
useEffect(() => {
19+
getAllMentorshipRequests().then(response => {
20+
if (response?.success) {
21+
setMentorshipRequests(
22+
response.data.filter(({ mentor, mentee }) => !!mentor && !!mentee)
23+
);
24+
}
25+
});
26+
}, []);
27+
28+
const sendEmail = async (mentorshipId: string) => {
29+
setMentorshipLoading(mentorshipId);
30+
await sendStaledRequestEmail(mentorshipId);
31+
setMentorshipRequests(
32+
mentorshipRequests.map(mentorship => {
33+
if (mentorship.id === mentorshipId) {
34+
return {
35+
...mentorship,
36+
reminderSentAt: new Date().toString(),
37+
};
38+
}
39+
return mentorship;
40+
})
41+
);
42+
setMentorshipLoading(null);
43+
toast.success('Email sent');
44+
};
45+
46+
const columns = [
47+
'Mentor',
48+
'Mentee',
49+
'Status',
50+
'Created',
51+
'Sent',
52+
].map(column => <td key={column}>{column}</td>);
53+
54+
const rows = mentorshipRequests
55+
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
56+
.map(({ mentor, mentee, status, date, id, reminderSentAt }) => {
57+
const pending = status === 'New' || status === 'Viewed';
58+
return (
59+
<tr key={id}>
60+
<td>{mentor.name}</td>
61+
<td>{mentee.name}</td>
62+
<td>{status}</td>
63+
<td>{pending ? formatRequestTime(new Date(date)) : 0}</td>
64+
<td>
65+
{reminderSentAt ? formatRequestTime(new Date(reminderSentAt)) : 0}
66+
</td>
67+
<td>
68+
{pending && (
69+
<Button
70+
disabled={!!reminderSentAt}
71+
title={reminderSentAt && `Sent at ${new Date(reminderSentAt)}`}
72+
isLoading={id === mentorshipLoading}
73+
onClick={() => sendEmail(id)}
74+
>
75+
Send Email
76+
</Button>
77+
)}
78+
</td>
79+
</tr>
80+
);
81+
});
82+
83+
return (
84+
<Card>
85+
{mentorshipRequests.length ? (
86+
<table>
87+
<thead>
88+
<tr>{columns}</tr>
89+
</thead>
90+
<tbody>{rows}</tbody>
91+
</table>
92+
) : (
93+
<>No requests</>
94+
)}
95+
</Card>
96+
);
97+
};
98+
99+
export default Admin;

src/Me/components/Button/Button.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type ButtonProps = Pick<
88
React.ButtonHTMLAttributes<HTMLButtonElement>,
99
HTMLButtonElement
1010
>,
11-
'onClick' | 'id' | 'disabled' | 'type' | 'name' | 'children'
11+
'onClick' | 'id' | 'disabled' | 'type' | 'name' | 'children' | 'title'
1212
> & {
1313
skin?: Skin;
1414
isLoading?: boolean;
@@ -23,6 +23,10 @@ const StyledButton = styled.button`
2323
line-height: 17px;
2424
text-align: center;
2525
cursor: pointer;
26+
27+
&:disabled {
28+
opacity: 0.5;
29+
}
2630
`;
2731

2832
const PrimaryButton = styled(StyledButton)`

src/Me/components/Card/index.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FC } from 'react';
22
import styled from 'styled-components';
3+
import { desktop, mobile } from '../../styles/shared/devices';
34

45
type CardProps = {
56
title?: string;
@@ -30,6 +31,7 @@ export const Content = styled.div`
3031
font-family: Lato;
3132
font-size: 1rem;
3233
font-family: Lato, 'Helvetica Neue', Arial, Helvetica, sans-serif;
34+
overflow-x: auto;
3335
`;
3436

3537
const CardContainer = styled.div`
@@ -38,6 +40,18 @@ const CardContainer = styled.div`
3840
position: relative;
3941
background-color: #ffffff;
4042
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.3);
43+
44+
@media ${desktop} {
45+
min-width: 400px;
46+
}
47+
48+
@media ${mobile} {
49+
max-width: 100%;
50+
/* & + & {
51+
margin-top: 30px;
52+
} */
53+
}
54+
4155
${Header} {
4256
padding-top: 20px;
4357
}
@@ -50,10 +64,6 @@ const CardContainer = styled.div`
5064
padding-right: var(--padding-inline);
5165
}
5266
53-
& + & {
54-
margin-top: 30px;
55-
}
56-
5767
h4 {
5868
color: #4a4a4a;
5969
line-height: 1.2142857143rem;

src/api/admin.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { makeApiCall, paths } from '.';
2+
import { MentorshipRequest } from '../types/models';
3+
4+
export function getAllMentorshipRequests() {
5+
return makeApiCall<MentorshipRequest[]>(`${paths.MENTORSHIP}/requests`);
6+
}
7+
8+
export async function sendStaledRequestEmail(mentorshipId: string) {
9+
await makeApiCall(
10+
`${paths.MENTORSHIP}/requests/${mentorshipId}/reminder`,
11+
null,
12+
'PUT'
13+
);
14+
}

0 commit comments

Comments
 (0)