Skip to content

Commit

Permalink
Merge pull request #42 from scffs/marks-by-day
Browse files Browse the repository at this point in the history
feat(marks-by-day): добавлены оценки на главную etc
  • Loading branch information
scffs authored Sep 8, 2023
2 parents ac15933 + 82eb73f commit 56a5d1e
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 117 deletions.
2 changes: 1 addition & 1 deletion backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ app.use(express.json())

const limiter = rateLimit({
windowMs: FIFTEEN_MINS_IN_MS,
max: 60,
max: 80,
statusCode: 429,
skipFailedRequests: true,
message: 'LIMIT'
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/LessonCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ const LessonCard: FC<ILessonCard> = ({ lesson }) => {
{timetable.teacher?.middleName[0]}
.
</div>
<div>
<div style={{ display: 'flex' }}>
{gradebook?.tasks?.map((task, index) => (
(task.isRequired || (Grade[setDefaultMark(task)] !== 'Д')) && (
<Mark
Expand Down
26 changes: 15 additions & 11 deletions frontend/src/components/MarksByGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import {
AbsenceType, Grade, PerformanceCurrent, TextMark, TMark,
} from '../../../shared';
import { getPerformance } from '../methods';

import Mark from './UI/Mark';
import calculateAverageMark from '../utils/calculateAverageMark';
import { useSnackbar } from '../hooks';

import Mark from './UI/Mark';

const THIRD_SEC = 30 * 1000;

const MarksByGroup = () => {
Expand All @@ -43,21 +43,17 @@ const MarksByGroup = () => {
if (!lastFetchTime || Date.now() - Number(lastFetchTime) >= THIRD_SEC || isHandle) {
const marks = await getPerformance();

localStorage.setItem('marks', JSON.stringify(marks));
localStorage.setItem('savedMarks', JSON.stringify(marks));
localStorage.setItem('lastFetchTime', String(Date.now()));

setMarksForSubject(marks);
setMarksForSubject(marks as PerformanceCurrent);
} else {
showSnackbar({
title: 'Оценки взяты из кеша',
onActionClick: () => fetchMarks(true),
action: 'Загрузить новые',
icon: <Icon28InfoCircle fill='var(--vkui--color_background_accent)' />,
});
const cachedMarks = localStorage.getItem('marks');
if (cachedMarks) {
setMarksForSubject(JSON.parse(cachedMarks));
}
}

setIsLoading(false);
Expand All @@ -74,7 +70,15 @@ const MarksByGroup = () => {
};

useEffect(() => {
fetchMarks();
const cachedMarks = localStorage.getItem('savedMarks');

if (cachedMarks) {
if (cachedMarks) {
setMarksForSubject(JSON.parse(cachedMarks));
}
} else {
fetchMarks();
}
}, []);

const subjectMarksMap: Record<string, { date: string; marks: TextMark[], absenceType?: AbsenceType }[]> = {};
Expand All @@ -84,7 +88,7 @@ const MarksByGroup = () => {
subjectMarksMap[subjectName] = [];
}

daysWithMarks.forEach((dayWithMark) => {
daysWithMarks?.forEach((dayWithMark) => {
subjectMarksMap[subjectName].push({
date: new Date(dayWithMark.day).toLocaleDateString(),
marks: dayWithMark.markValues,
Expand Down Expand Up @@ -120,7 +124,7 @@ const MarksByGroup = () => {
before={<Icon20StatisticsOutline />}
style={{ marginTop: 5 }}
after={calculateAverageMark(
marksForSubject.daysWithMarksForSubject[i].daysWithMarks.reduce(
marksForSubject.daysWithMarksForSubject[i].daysWithMarks?.reduce(
(allMarks, day) => [...allMarks, ...day.markValues],
[] as TextMark[],
),
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/UI/CalendarRange.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
FC, useRef, useState, useEffect,
FC, useRef, useState, useEffect, ReactNode,
} from 'react';
import {
Button,
Expand All @@ -14,7 +14,7 @@ import {
import PropTypes from 'prop-types';

interface CalendarRangeProps {
label: string;
label: string | ReactNode;
onDateChange?: (newDate: Date) => void;
value?: Date;
}
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { CSSProperties, FC } from 'react';

import { TMark } from '../../../../shared';
import { Footnote } from '@vkontakte/vkui';
import { TMark } from '../../../../../shared';

type Sizes = 'l' | 's';

interface IMark {
mark: TMark;
size?: Sizes;
bottom?: string;
useMargin?: boolean;
style?: CSSProperties;
}

const getBackgroundColor = (score: TMark) => {
Expand All @@ -19,7 +22,9 @@ const getBackgroundColor = (score: TMark) => {
return '#DA0A35';
};

const Mark: FC<IMark> = ({ mark, size = 'l', useMargin = true }) => {
const Mark: FC<IMark> = ({
mark, size = 'l', useMargin = true, bottom, style,
}) => {
const getSize = (size: Sizes) => {
if (size === 's') {
return '1rem';
Expand All @@ -39,8 +44,15 @@ const Mark: FC<IMark> = ({ mark, size = 'l', useMargin = true }) => {
display: 'inline-block',
};
return (
<div style={styles}>
{mark}
<div style={{ ...style }}>
<div style={styles}>
{mark}
</div>
{bottom && (
<Footnote style={{ padding: 3 }}>
{bottom}
</Footnote>
)}
</div>
);
};
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/UI/MarksByDay/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.marksByName {
display: flex;
margin-left: 10px;
}
66 changes: 66 additions & 0 deletions frontend/src/components/UI/MarksByDay/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { FC } from 'react';
import {
Group, Header, HorizontalCell, HorizontalScroll,
} from '@vkontakte/vkui';

import { Grade, PerformanceCurrent } from '../../../../../shared';

import Mark from '../Mark';

import './index.css';

interface IPerformanceCurrent {
performanceData: PerformanceCurrent | null;
}

const truncateString = (str: string, maxLength: number) => {
if (str.length > maxLength) {
return `${str.substring(0, maxLength)}...`;
}
return str;
};

const MarksByDay: FC<IPerformanceCurrent> = ({ performanceData }) => {
const marksByDay: { [key: string]: { grades: Grade[]; lessonName: string } } = {};

performanceData?.daysWithMarksForSubject?.length !== undefined && performanceData?.daysWithMarksForSubject?.length > 0 && performanceData?.daysWithMarksForSubject.forEach((subject) => {
subject?.daysWithMarks?.forEach((dayWithMarks) => {
const day = new Date(dayWithMarks.day).toLocaleDateString();
const grades = dayWithMarks.markValues.map((gradeText) => Grade[gradeText]);
const lessonName = subject.subjectName;
if (!marksByDay[day]) {
marksByDay[day] = { grades: [], lessonName: '' };
}

marksByDay[day].grades = [...marksByDay[day].grades, ...grades];
marksByDay[day].lessonName = lessonName;
});
});

return (
<HorizontalScroll
showArrows
getScrollToLeft={(i) => i - 120}
getScrollToRight={(i) => i + 120}
>
<Group header={<Header mode='secondary'>Недавние оценки</Header>}>
<div className='marksByName'>
{Object.entries(marksByDay).map(([day, { grades, lessonName }], index) => (
<div>
<Header mode='secondary'>{day}</Header>
<div key={index} style={{ display: 'flex' }}>
{grades.map((grade, gradeIndex) => (
<HorizontalCell style={{ maxWidth: 'unset' }}>
<Mark style={{ maxWidth: 90 }} key={gradeIndex} mark={grade} size='l' bottom={truncateString(lessonName, 18)} useMargin={false} />
</HorizontalCell>
))}
</div>
</div>
))}
</div>
</Group>
</HorizontalScroll>
);
};

export default MarksByDay;
6 changes: 3 additions & 3 deletions frontend/src/components/UI/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import {
Icon28GraphOutline,
Icon28SettingsOutline,
Icon28BookSpreadOutline,
Icon28HomeOutline,
Icon28HelpOutline,
} from '@vkontakte/icons';

Expand All @@ -31,9 +31,9 @@ const Sidebar: FC<ISidebarProps> = ({ activeView, onStoryChange }) => (
disabled={activeView === VIEW_SCHEDULE}
onClick={() => onStoryChange(VIEW_SCHEDULE)}
style={activeView === VIEW_SCHEDULE ? activeStoryStyles : undefined}
before={<Icon28BookSpreadOutline />}
before={<Icon28HomeOutline />}
>
Расписание
Главная
</Cell>
<Cell
disabled={activeView === VIEW_MARKS}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/UI/Tabbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC } from 'react';
import { Tabbar as VKUITabbar, TabbarItem, useAdaptivityConditionalRender } from '@vkontakte/vkui';

import {
Icon28BookSpreadOutline,
Icon28HomeOutline,
Icon28HelpOutline,
Icon28GraphOutline,
Icon28SettingsOutline,
Expand All @@ -28,9 +28,9 @@ const Tabbar: FC<ITabbar> = ({ onStoryChange, activeView }) => {
onClick={() => onStoryChange(VIEW_SCHEDULE)}
selected={activeView === VIEW_SCHEDULE}
data-story={VIEW_SCHEDULE}
text='Расписание'
text='Главная'
>
<Icon28BookSpreadOutline />
<Icon28HomeOutline />
</TabbarItem>
<TabbarItem
onClick={() => onStoryChange(VIEW_MARKS)}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/methods/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { getUserId } from './bridge/getUserId';

import { getPerformance } from './server/getPerformance.ts';
import { getLessons } from './server/getLessons';
import { getMarks } from './server/getMarks';
// import { getMarks } from './server/getMarks';

export {
getCookie, getMarks, getUserId, getLessons, appStorageSet, getPerformance, getVkStorageData, getVkStorageKeys,
getCookie, getUserId, getLessons, appStorageSet, getPerformance, getVkStorageData, getVkStorageKeys,
};
48 changes: 24 additions & 24 deletions frontend/src/methods/server/getMarks.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { IMark } from '../../../../shared';

import { getCookie } from '../bridge/getCookie';
import { getUserId } from '../bridge/getUserId';

// TODO: возможно придётся удалить
export const getMarks = async (): Promise<IMark> => {
const cookie = await getCookie();
const id = await getUserId();

const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/dashboard/${id}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
secret: cookie as string,
},
});

if (!response.ok) {
throw new Error('Failed to fetch lessons');
}

return await response.json() as IMark;
};
// import { IMark } from '../../../../shared';
//
// import { getCookie } from '../bridge/getCookie';
// import { getUserId } from '../bridge/getUserId';
//
// // TODO: возможно придётся удалить
// export const getMarks = async (): Promise<IMark> => {
// const cookie = await getCookie();
// const id = await getUserId();
//
// const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/dashboard/${id}`, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json;charset=UTF-8',
// secret: cookie as string,
// },
// });
//
// if (!response.ok) {
// throw new Error('Failed to fetch lessons');
// }
//
// return await response.json() as IMark;
// };
7 changes: 6 additions & 1 deletion frontend/src/methods/server/getPerformance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { PerformanceCurrent } from '../../../../shared';
import { getCookie } from '../bridge/getCookie';
import { getUserId } from '../bridge/getUserId';

export const getPerformance = async (): Promise<PerformanceCurrent> => {
export const getPerformance = async (): Promise<PerformanceCurrent | number> => {
const cookie = await getCookie();
const id = await getUserId();
console.log('getUserId', id);
Expand All @@ -15,6 +15,11 @@ export const getPerformance = async (): Promise<PerformanceCurrent> => {
},
});

if (response.status === 429) {
console.log(response.status);
return response.status;
}

if (!response.ok) {
throw new Error('Failed to fetch lessons');
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/utils/calculateAverageMark.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Grade, TextMark } from '../../../shared';

const calculateAverageMark = (marks: TextMark[]): number | null => {
if (marks.length === 0) {
const calculateAverageMark = (marks: TextMark[] | undefined): number | null => {
if (!marks || marks.length === 0) {
return null;
}

Expand Down
Loading

0 comments on commit 56a5d1e

Please sign in to comment.