Skip to content

Commit

Permalink
feat: move endorsement modal to Profile page (#2283)
Browse files Browse the repository at this point in the history
  • Loading branch information
apalchys authored Sep 8, 2023
1 parent 38d2b64 commit 8c62cfa
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 132 deletions.
225 changes: 110 additions & 115 deletions client/src/components/Profile/MentorStatsCard.tsx
Original file line number Diff line number Diff line change
@@ -1,134 +1,129 @@
import * as React from 'react';
import isEqual from 'lodash/isEqual';
import { useMemo, useState } from 'react';
import { List, Typography, Button, Tag } from 'antd';
import CommonCard from './CommonCard';
import MentorStatsModal from './MentorStatsModal';
import { MentorStats, Student } from 'common/models/profile';
import { TeamOutlined, FullscreenOutlined } from '@ant-design/icons';
import { TeamOutlined, FullscreenOutlined, FileTextOutlined } from '@ant-design/icons';
import { MentorEndorsement } from 'modules/Profile/components/MentorEndorsement';

const { Text } = Typography;

type Props = {
isAdmin?: boolean;
githubId: string;
data: MentorStats[];
};

type State = {
courseIndex: number;
isMentorStatsModalVisible: boolean;
};

class MentorStatsCard extends React.Component<Props, State> {
state = {
courseIndex: 0,
isMentorStatsModalVisible: false,
};
export function MentorStatsCard(props: Props) {
const [courseIndex, setCourseIndex] = useState(0);
const [isMentorStatsModalVisible, setIsMentorStatsModalVisible] = useState(false);
const [isEndorsmentModalVisible, setIsEndorsmentModalVisible] = useState(false);

private showMentorStatsModal = (courseIndex: number) => {
this.setState({ courseIndex, isMentorStatsModalVisible: true });
const showMentorStatsModal = (courseIndex: number) => {
setCourseIndex(courseIndex);
setIsMentorStatsModalVisible(true);
};

private hideMentortStatsModal = () => {
this.setState({ isMentorStatsModalVisible: false });
const hideMentortStatsModal = () => {
setIsMentorStatsModalVisible(false);
};

private countStudents = (data: MentorStats[]) =>
data.reduce((acc: Student[], cur: MentorStats) => (cur?.students?.length ? acc.concat(cur.students) : acc), [])
.length;
const stats = props.data;
const count = useMemo(
() => props.data.reduce<Student[]>((acc, cur) => acc.concat(cur.students ?? []), []).length,
[],
);

shouldComponentUpdate = (_nextProps: Props, nextState: State) =>
!isEqual(nextState.isMentorStatsModalVisible, this.state.isMentorStatsModalVisible);

render() {
const stats = this.props.data;
const { courseIndex, isMentorStatsModalVisible } = this.state;

return (
<>
<MentorStatsModal
stats={stats[courseIndex]}
isVisible={isMentorStatsModalVisible}
onHide={this.hideMentortStatsModal}
/>
<CommonCard
title="Mentor Statistics"
icon={<TeamOutlined />}
content={
<>
<div>
<p>
Mentored Students:{' '}
<Text style={{ fontSize: 18 }} strong>
{this.countStudents(stats)}
</Text>
</p>
<p>
Courses as Mentor:{' '}
<Text style={{ fontSize: 18 }} strong>
{stats.length}
</Text>
</p>
</div>
<List
itemLayout="horizontal"
dataSource={stats}
renderItem={({ courseName, courseLocationName, students }, idx) => (
<List.Item style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ flexGrow: 2 }}>
<p style={{ fontSize: idx === 0 ? 20 : undefined, marginBottom: 5 }}>
<Text strong>
{courseName}
{courseLocationName && ` / ${courseLocationName}`}
</Text>
</p>
{students ? (
idx === 0 && (
<List
itemLayout="horizontal"
dataSource={students}
renderItem={({ githubId, name, isExpelled, totalScore }) => (
<List.Item style={{ display: 'flex', justifyContent: 'space-between' }}>
<div key={`mentor-students-${githubId} ${courseName}`} style={{ width: '100%' }}>
<p
style={{
display: 'flex',
justifyContent: 'space-between',
fontSize: 12,
marginBottom: 5,
}}
>
<a href={`/profile?githubId=${githubId}`}>{name}</a>{' '}
{isExpelled ? <Tag color="red">expelled</Tag> : <Tag color="green">active</Tag>}
</p>
<p style={{ fontSize: 12, marginBottom: 0 }}>
Score: <Text mark>{totalScore}</Text>
</p>
</div>
</List.Item>
)}
/>
)
) : (
<p>Does not have students at this course yet</p>
)}
</div>
{students && (
<Button
style={{ marginLeft: 16 }}
type="dashed"
onClick={this.showMentorStatsModal.bind(null, idx)}
>
<FullscreenOutlined />
</Button>
return (
<>
<MentorStatsModal
stats={stats[courseIndex]}
isVisible={isMentorStatsModalVisible}
onHide={hideMentortStatsModal}
/>
<MentorEndorsement
onClose={() => setIsEndorsmentModalVisible(false)}
open={isEndorsmentModalVisible}
githubId={props.githubId}
/>
<CommonCard
title="Mentor Statistics"
icon={<TeamOutlined />}
content={
<>
<div>
<p>
Mentored Students:{' '}
<Text style={{ fontSize: 18 }} strong>
{count}
</Text>
</p>
<p>
Courses as Mentor:{' '}
<Text style={{ fontSize: 18 }} strong>
{stats.length}
</Text>
</p>
</div>
{props.isAdmin ? (
<Button onClick={() => setIsEndorsmentModalVisible(true)} icon={<FileTextOutlined />} size="small">
Get Endorsment
</Button>
) : null}
<List
itemLayout="horizontal"
dataSource={stats}
renderItem={({ courseName, courseLocationName, students }, idx) => (
<List.Item style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ flexGrow: 2 }}>
<p style={{ fontSize: idx === 0 ? 20 : undefined, marginBottom: 5 }}>
<Text strong>
{courseName}
{courseLocationName && ` / ${courseLocationName}`}
</Text>
</p>
{students ? (
idx === 0 && (
<List
itemLayout="horizontal"
dataSource={students}
renderItem={({ githubId, name, isExpelled, totalScore }) => (
<List.Item style={{ display: 'flex', justifyContent: 'space-between' }}>
<div key={`mentor-students-${githubId} ${courseName}`} style={{ width: '100%' }}>
<p
style={{
display: 'flex',
justifyContent: 'space-between',
fontSize: 12,
marginBottom: 5,
}}
>
<a href={`/profile?githubId=${githubId}`}>{name}</a>{' '}
{isExpelled ? <Tag color="red">expelled</Tag> : <Tag color="green">active</Tag>}
</p>
<p style={{ fontSize: 12, marginBottom: 0 }}>
Score: <Text mark>{totalScore}</Text>
</p>
</div>
</List.Item>
)}
/>
)
) : (
<p>Does not have students at this course yet</p>
)}
</List.Item>
)}
/>
</>
}
/>
</>
);
}
</div>
{students && (
<Button style={{ marginLeft: 16 }} type="dashed" onClick={showMentorStatsModal.bind(null, idx)}>
<FullscreenOutlined />
</Button>
)}
</List.Item>
)}
/>
</>
}
/>
</>
);
}

export default MentorStatsCard;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import MentorStatsCard from '../MentorStatsCard';
import { MentorStatsCard } from '../MentorStatsCard';

describe('MentorStatsCard', () => {
const mentorStats = [
Expand Down Expand Up @@ -31,7 +31,7 @@ describe('MentorStatsCard', () => {
];

it('should render correctly', () => {
const { container } = render(<MentorStatsCard data={mentorStats} />);
const { container } = render(<MentorStatsCard githubId="test" data={mentorStats} />);
expect(container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Input, Modal, Spin, Typography } from 'antd';
import { Alert, Input, Modal, Spin, Typography } from 'antd';
import { useMemo } from 'react';
import { ProfileApi } from 'api';
import { useAsync } from 'react-use';
Expand All @@ -7,31 +7,42 @@ import isObject from 'lodash/isObject';
import omitBy from 'lodash/omitBy';

export interface Props {
githubId: string | null;
open: boolean;
githubId: string;
onClose: () => void;
}

const api = new ProfileApi();

export function MentorEndorsement(props: Props) {
const { value, loading } = useAsync(async () => {
if (props.githubId) {
const { value, loading, error } = useAsync(async () => {
if (props.open) {
const { data } = await api.getEndorsement(props.githubId);
return data;
}
}, [props.githubId]);
}, [props.githubId, props.open]);

const data = useMemo(() => (value?.data ? cleanData(value.data) : null), [value?.data]);

return (
<Modal
width={640}
onCancel={props.onClose}
cancelButtonProps={{ hidden: true }}
onOk={props.onClose}
open={Boolean(props.githubId)}
open={props.open}
>
<Spin spinning={loading}>
<div style={{ minHeight: 320 }}>
<div style={{ minHeight: 320, paddingTop: 32 }}>
{error ? (
<Alert
closable={false}
message="Error occurred while generating endorsment"
description={error.message}
type="error"
/>
) : null}

{value ? (
<>
<Typography.Title level={4}>Generated Text</Typography.Title>
Expand All @@ -46,6 +57,7 @@ export function MentorEndorsement(props: Props) {
</>
) : null}
</div>

{data ? (
<div>
<Typography.Title level={4}>Data Model</Typography.Title>
Expand Down
6 changes: 0 additions & 6 deletions client/src/pages/course/admin/mentors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { AssignStudentModal } from 'components/Student';
import { PersonCell, getColumnSearchProps, numberSorter, stringSorter } from 'components/Table';
import withCourseData from 'components/withCourseData';
import { Session, withSession } from 'components/withSession';
import { MentorEndorsement } from 'modules/Mentor/components/MentorEndorsement';
import { MenuInfo } from 'rc-menu/lib/interface';
import { useMemo, useState } from 'react';
import { useAsync } from 'react-use';
Expand Down Expand Up @@ -53,7 +52,6 @@ function Page(props: CoursePageProps) {
const [loading, setLoading] = useState(false);
const [stats, setStats] = useState(null as Stats | null);
const [mentors, setMentors] = useState<MentorDetailsDto[]>([]);
const [endorsementGithubId, setEndorsementGithubId] = useState<string | null>(null);
const [currentMentor, setCurrentMentor] = useState<string | null>(null);
const [modal, contextHolder] = Modal.useModal();

Expand Down Expand Up @@ -151,9 +149,6 @@ function Page(props: CoursePageProps) {
title: 'Do you want to restore the mentor?',
});
break;
case 'endorsment':
setEndorsementGithubId(mentor.githubId);
break;
}
};

Expand Down Expand Up @@ -306,7 +301,6 @@ function Page(props: CoursePageProps) {
},
]}
/>
<MentorEndorsement onClose={() => setEndorsementGithubId(null)} githubId={endorsementGithubId} />
<AssignStudentModal
onClose={() => setCurrentMentor(null)}
courseId={courseId}
Expand Down
9 changes: 7 additions & 2 deletions client/src/pages/profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import EducationCard from 'components/Profile/EducationCard';
import ContactsCard from 'components/Profile/ContactsCard';
import PublicFeedbackCard from 'components/Profile/PublicFeedbackCard';
import StudentStatsCard from 'components/Profile/StudentStatsCard';
import MentorStatsCard from 'components/Profile/MentorStatsCard';
import { MentorStatsCard } from 'components/Profile/MentorStatsCard';
import CoreJsIviewsCard from 'components/Profile/CoreJsIviewsCard';
import LanguagesCard from 'components/Profile/LanguagesCard';
import { CoreJsInterviewsData } from 'components/Profile/CoreJsIviewsCard';
Expand Down Expand Up @@ -196,6 +196,9 @@ export class ProfilePage extends React.Component<Props, State> {
const aboutMyself = profile?.generalInfo?.aboutMyself ?? '';
const languages = profile?.generalInfo?.languages ?? [];

const githubId = this.props.session.githubId;
const isAdmin = this.props.session.isAdmin;

const cards = [
profile?.generalInfo && (
<MainCard data={mainInfo} isEditingModeEnabled={isProfileOwner} updateProfile={this.updateProfile} />
Expand Down Expand Up @@ -237,7 +240,9 @@ export class ProfilePage extends React.Component<Props, State> {
isProfileOwner={isProfileOwner}
/>
),
profile?.mentorStats?.length && <MentorStatsCard data={profile.mentorStats} />,
profile?.mentorStats?.length && githubId && (
<MentorStatsCard isAdmin={isAdmin} githubId={githubId} data={profile.mentorStats} />
),
profile?.studentStats?.length && this.hadStudentCoreJSInterview(profile.studentStats) && (
<CoreJsIviewsCard data={this.getStudentCoreJSInterviews(profile.studentStats)} />
),
Expand Down

0 comments on commit 8c62cfa

Please sign in to comment.