Skip to content

Commit

Permalink
feat: add Mentor Statistics (#2417)
Browse files Browse the repository at this point in the history
* feat: add Mentor Statistics

* refactor: code review
  • Loading branch information
valerydluski authored Feb 7, 2024
1 parent 3972cd4 commit 2bfb224
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 57 deletions.
185 changes: 168 additions & 17 deletions client/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,19 @@ export interface ContactsDto {
*/
'discord'?: string | null;
}
/**
*
* @export
* @interface CountriesStatsDto
*/
export interface CountriesStatsDto {
/**
*
* @type {Array<CountryStatDto>}
* @memberof CountriesStatsDto
*/
'countries': Array<CountryStatDto>;
}
/**
*
* @export
Expand All @@ -585,13 +598,13 @@ export interface CountryStatDto {
* @type {string}
* @memberof CountryStatDto
*/
'country': string;
'countryName': string;
/**
*
* @type {number}
* @memberof CountryStatDto
*/
'studentsCount': number;
'count': number;
}
/**
*
Expand Down Expand Up @@ -829,6 +842,31 @@ export const CourseEventDtoTypeEnum = {

export type CourseEventDtoTypeEnum = typeof CourseEventDtoTypeEnum[keyof typeof CourseEventDtoTypeEnum];

/**
*
* @export
* @interface CourseMentorsStatsDto
*/
export interface CourseMentorsStatsDto {
/**
*
* @type {number}
* @memberof CourseMentorsStatsDto
*/
'mentorsActiveCount': number;
/**
*
* @type {number}
* @memberof CourseMentorsStatsDto
*/
'mentorsTotalCount': number;
/**
*
* @type {number}
* @memberof CourseMentorsStatsDto
*/
'epamMentorsCount': number;
}
/**
*
* @export
Expand Down Expand Up @@ -5019,19 +5057,6 @@ export interface StudentId {
*/
'id': number;
}
/**
*
* @export
* @interface StudentsCountriesStatsDto
*/
export interface StudentsCountriesStatsDto {
/**
*
* @type {Array<CountryStatDto>}
* @memberof StudentsCountriesStatsDto
*/
'countries': Array<CountryStatDto>;
}
/**
*
* @export
Expand Down Expand Up @@ -7988,6 +8013,72 @@ export class CourseMentorsApi extends BaseAPI {
*/
export const CourseStatsApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCourseMentorCountries: async (courseId: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'courseId' is not null or undefined
assertParamExists('getCourseMentorCountries', 'courseId', courseId)
const localVarPath = `/courses/{courseId}/stats/mentors/countries`
.replace(`{${"courseId"}}`, encodeURIComponent(String(courseId)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}

const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;



setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCourseMentors: async (courseId: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'courseId' is not null or undefined
assertParamExists('getCourseMentors', 'courseId', courseId)
const localVarPath = `/courses/{courseId}/stats/mentors`
.replace(`{${"courseId"}}`, encodeURIComponent(String(courseId)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}

const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;



setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {number} courseId
Expand Down Expand Up @@ -8064,6 +8155,26 @@ export const CourseStatsApiAxiosParamCreator = function (configuration?: Configu
export const CourseStatsApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = CourseStatsApiAxiosParamCreator(configuration)
return {
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getCourseMentorCountries(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CountriesStatsDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseMentorCountries(courseId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getCourseMentors(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CourseMentorsStatsDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseMentors(courseId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {number} courseId
Expand All @@ -8080,7 +8191,7 @@ export const CourseStatsApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getCourseStudentCountries(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<StudentsCountriesStatsDto>> {
async getCourseStudentCountries(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CountriesStatsDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStudentCountries(courseId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
Expand All @@ -8094,6 +8205,24 @@ export const CourseStatsApiFp = function(configuration?: Configuration) {
export const CourseStatsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = CourseStatsApiFp(configuration)
return {
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCourseMentorCountries(courseId: number, options?: any): AxiosPromise<CountriesStatsDto> {
return localVarFp.getCourseMentorCountries(courseId, options).then((request) => request(axios, basePath));
},
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCourseMentors(courseId: number, options?: any): AxiosPromise<CourseMentorsStatsDto> {
return localVarFp.getCourseMentors(courseId, options).then((request) => request(axios, basePath));
},
/**
*
* @param {number} courseId
Expand All @@ -8109,7 +8238,7 @@ export const CourseStatsApiFactory = function (configuration?: Configuration, ba
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCourseStudentCountries(courseId: number, options?: any): AxiosPromise<StudentsCountriesStatsDto> {
getCourseStudentCountries(courseId: number, options?: any): AxiosPromise<CountriesStatsDto> {
return localVarFp.getCourseStudentCountries(courseId, options).then((request) => request(axios, basePath));
},
};
Expand All @@ -8122,6 +8251,28 @@ export const CourseStatsApiFactory = function (configuration?: Configuration, ba
* @extends {BaseAPI}
*/
export class CourseStatsApi extends BaseAPI {
/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof CourseStatsApi
*/
public getCourseMentorCountries(courseId: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCourseMentorCountries(courseId, options).then((request) => request(this.axios, this.basePath));
}

/**
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof CourseStatsApi
*/
public getCourseMentors(courseId: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCourseMentors(courseId, options).then((request) => request(this.axios, this.basePath));
}

/**
*
* @param {number} courseId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { Flex, Image, Typography } from 'antd';
import { CountryStatDto } from 'api';
import { useCallback, useMemo } from 'react';

export enum Colors {
purple = '#9c80f7',
}

type Props = {
data: CountryStatDto[];
studentsActiveCount: number;
activeCount: number;
xAxisTitle: string;
color?: keyof typeof Colors;
};

/*
Expand All @@ -18,29 +24,32 @@ type Datum = Parameters<typeof Bar>[0]['data'][number];

const { Text } = Typography;

function StudentsCountriesChart({ data, studentsActiveCount }: Props) {
function CountriesChart({ data, activeCount, xAxisTitle, color }: Props) {
const tooltipFormatter = useCallback(
(datum: Datum) => {
const percentage = studentsActiveCount ? Math.ceil((datum.studentsCount / studentsActiveCount) * 100) : 0;
const percentage = activeCount ? Math.ceil((datum.count / activeCount) * 100) : 0;
return {
name: 'Number of Students',
value: `${datum.studentsCount} (${percentage}%)`,
name: xAxisTitle,
value: `${datum.count} (${percentage}%)`,
};
},
[studentsActiveCount],
[activeCount],
);

const config: BarConfig = useMemo(
() => ({
data,
yField: 'country',
xField: 'studentsCount',
yField: 'countryName',
xField: 'count',
yAxis: {
label: { autoRotate: false },
},
tooltip: { formatter: tooltipFormatter },
xAxis: { title: { text: 'Number of Students' } },
xAxis: { title: { text: xAxisTitle } },
scrollbar: { type: 'vertical' },
//Why this affects the size of the chart, I don't know. Do not delete.
seriesField: 'type',
color: () => color || '#1890ff',
}),
[data, tooltipFormatter],
);
Expand All @@ -57,4 +66,4 @@ function StudentsCountriesChart({ data, studentsActiveCount }: Props) {
return <Bar {...config} />;
}

export default StudentsCountriesChart;
export default CountriesChart;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Card } from 'antd';
import { CountriesStatsDto } from 'api';
import dynamic from 'next/dynamic';

type Props = {
countriesStats: CountriesStatsDto;
activeCount: number;
};

const CountriesChart = dynamic(() => import('../CountriesChart/CountriesChart'), { ssr: false });

export const MentorsCountriesCard = ({ countriesStats, activeCount }: Props) => {
const { countries } = countriesStats;
return (
<Card title="Mentors Countries Stats">
<div style={{ height: 400, width: '100%' }}>
<CountriesChart data={countries} activeCount={activeCount} xAxisTitle={'Number of Students'} color={'purple'} />
</div>
</Card>
);
};
Empty file.
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Card } from 'antd';
import { StudentsCountriesStatsDto } from 'api';
import { CountriesStatsDto } from 'api';
import dynamic from 'next/dynamic';

type Props = {
studentsCountriesStats: StudentsCountriesStatsDto;
studentsCountriesStats: CountriesStatsDto;
studentsActiveCount: number;
};

const StudentsCountriesChart = dynamic(() => import('./StudentsCountriesChart'), { ssr: false });
const CountriesChart = dynamic(() => import('../CountriesChart/CountriesChart'), { ssr: false });

export const StudentsCountriesCard = ({ studentsCountriesStats, studentsActiveCount }: Props) => {
const { countries } = studentsCountriesStats;
return (
<Card title="Students Countries Stats">
<div style={{ height: 400, width: '100%' }}>
<StudentsCountriesChart data={countries} studentsActiveCount={studentsActiveCount} />
<CountriesChart data={countries} activeCount={studentsActiveCount} xAxisTitle={'Number of Students'} />
</div>
</Card>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ const courseStatsApi = new CourseStatsApi();
export function useCourseStats(courseId: number) {
return useAsync(async () => {
try {
const [studentsCountries, studentsStats] = await Promise.all([
const [studentsCountries, studentsStats, mentorsCountries, mentorsStats] = await Promise.all([
courseStatsApi.getCourseStudentCountries(courseId),
courseStatsApi.getCourseStats(courseId),
courseStatsApi.getCourseMentorCountries(courseId),
courseStatsApi.getCourseMentors(courseId),
]);
return {
studentsCountries: studentsCountries.data,
studentsStats: studentsStats.data,
mentorsCountries: mentorsCountries.data,
mentorsStats: mentorsStats.data,
};
} catch (error) {
message.error('Something went wrong, please try to reload the page later');
Expand Down
Loading

0 comments on commit 2bfb224

Please sign in to comment.