Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vladimir fyodorov/small landing fixes #49

Merged
merged 5 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/assets/translations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,8 @@
"navTubsPractice": "Задание",
"navTubsResults": "Итоги",
"navToLessons": "Все уроки",
"deadlineUploadText": "Дедлайн: ",
"deadlineUploadTime": " 23:59\u00A0по\u00A0Мск",
"deadlineResultsText": "Публикация итогов: ",
"uploadDeadline": "Дедлайн: %{date}\u00A0по\u00A0Мск",
"resultsDeadline": "Публикация итогов: %{date}",
"uploadBtn": "Загрузить работу",
"resultLinkTitle": "Результаты задания",
"editTitle": "Редактировать...",
Expand Down Expand Up @@ -201,7 +200,10 @@
"showMoreBtn": "показать больше",
"showLessBtn": "свернуть"
},
"fallback:lessonNotStartedYet": "Этот урок пока не начался. Он начнётся %{startDate}, будем вас ждать"
"fallback:lessonNotStartedYet": "Этот урок пока не начался. Он начнётся %{startDate}, будем вас ждать",
"fallback:noResults:beforeDeadline": "Дата публикации результатов %{resultsEndDate}. Приходите позже.",
"fallback:noResults:deadlineDay": "Результатов пока нет, но они должны появится сегодня.",
"fallback:noResults:pastDeadline": "Извиняемся за задержку результатов, мы их активно проверяем. Если задержка более дня, напишите, пожалуйста, в телеграм-чат курса."
},
"week": {
"one": "неделя",
Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export * from './const';
export * from './fetch';
export * from './getId';
export * from './guid';
export * from './isMounted';
export * from './memoize';
export * from './URLSection';
13 changes: 13 additions & 0 deletions src/hooks/isMounted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useEffect, useRef } from 'react';

export function useIsMounted() {
const isMounted = useRef(true);

useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);

return isMounted;
}
5 changes: 2 additions & 3 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';

Expand All @@ -13,9 +12,9 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
store.dispatch(init({}));

root.render(
<React.StrictMode>
// <React.StrictMode> // fucks up isMounted hook
<Provider store={store}>
<Router/>
</Provider>
</React.StrictMode>
// </React.StrictMode>
);
2 changes: 1 addition & 1 deletion src/pages/Course/Landing/DiscountBanner/DiscountBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function DiscountBanner(props: IProps) {
</div>
</div>
<div className={classes.descriptionWrapper + ' s-text-24'}>
{t('description', { deadline: formatDate(discontDeadline, { timeZone: 'Europe/Moscow' }) })}
{t('description', { deadline: formatDate(discontDeadline, { timeZone: 'Europe/Moscow', wTime: true }) })}
</div>
</div>
<div className={classes.timerWrapper}>
Expand Down
29 changes: 19 additions & 10 deletions src/pages/Course/Lesson/Lesson.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@ import LessonUppload from './LessonUppload/LessonUppload';
import LessonWorks from './LessonWorks/LessonWorks';
import LessonHeader from './LessonHeader/LessonHeader';

import useCanShowResults from './useCanShowResults';
import useFetchHomework from './useFetchHomework';
import useHomeworkFallback from './useHomeworkFallback';
import useInitHomework from './useInitHomework';
import useLessonFallback from './useLessonFallback';

import type { IHomeworksState, ILessonState, IRootState } from 'types';
import type { ILessonState, IRootState } from 'types';
import Fallback from 'ui/Fallback';
import { i18n } from 'shared';
import { userService } from 'services/user.service';

export default connect(mapStateToProps)(Lesson);

interface IConnectedProps {
lessonState: ILessonState
homeworksState: IHomeworksState
authedUserId?: string
}

function mapStateToProps(state: IRootState): IConnectedProps {
return {
lessonState: state.lesson,
homeworksState: state.homeworks,
authedUserId: state.user?.user?.id,
};
}

Expand All @@ -39,11 +39,15 @@ interface IProps extends IConnectedProps {
}

function Lesson(props: IProps) {
const { lessonState, section, authedUserId } = props;
const { lessonState, section } = props;
const now = new Date();

const { courseId, lessonId } = useParams();
const [scrollToUpload, setScrollToUpload] = useState<boolean>(false);

const authedUser = userService.useAuthedUser();
const authedUserId = authedUser?.id;

useFetch<IFetchLessonPayload>(({
actionCreator: fetchLesson,
payload: {
Expand All @@ -55,17 +59,22 @@ function Lesson(props: IProps) {
useInitHomework({ courseId, lessonId, userId: authedUserId, lesson: lessonState.data });
const { homework, homeworkState } = useFetchHomework({ courseId, lessonId, userId: authedUserId });

const fallback = useLessonFallback(lessonState);
const fallback = useLessonFallback({ lessonState, authedUser });
const homeworkFallback = useHomeworkFallback(homeworkState);
const { canShowResults, fallBack: resultsFallback } = useCanShowResults({ courseId, lessonId, lesson: lessonState.data })

if (!lessonState.data || lessonState.data.startDate > new Date()) {
if (!lessonState.data || (authedUser?.role !== 'support' && lessonState.data.startDate > now)) {
return fallback;
}

if (lessonState.data.type === 'Practice' && !homework) {
return homeworkFallback;
}

if (section === 'results' && !canShowResults) {
return resultsFallback;
}

return (
<Page header wrapper='Lesson'>
<LessonHeader
Expand All @@ -82,13 +91,13 @@ function Lesson(props: IProps) {
scrollToUpload={() => setScrollToUpload(true)}
/>)
}
{section === 'task' && homework?.homework?.state === 'DRAFT' &&
{section === 'task' && lessonState.data.endDate > now && homework?.homework?.state === 'DRAFT' &&
<LessonUppload
homeworkWPopulate={homework}
scroll={scrollToUpload}
onScrollEnd={() => setScrollToUpload(false)}
/>
}
{section === 'results' && <LessonWorks/>}
{section === 'results' && canShowResults && <LessonWorks/>}
</Page>);
}
28 changes: 4 additions & 24 deletions src/pages/Course/Lesson/LessonContent/LessonContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import classes from './LessonContent.module.scss';
import { URLSections } from 'types';
import type { IHomeworkDataWPopulate, ILessonContent, ILessonData } from 'types';
import { homeworkService } from 'services';
import { formatDate } from 'utils';

export default LessonContent;

Expand Down Expand Up @@ -57,13 +58,13 @@ function Uppload(props: IUpploadProps) {
<Fragment>
<div className={classes.uploadDeadline}>
<p className='s-text-24'>
{t('deadlineUploadText')} {getWeekDay(endDate)} {formatLessonDate(endDate)} {t('deadlineUploadTime')}
{t('uploadDeadline', { date: formatDate(endDate, { timeZone: 'Europe/Moscow', wWeekDay: true, wTime: true }) })}
</p>
<p className='s-text-24'>
{t('deadlineResultsText')} {getWeekDay(resultsEndDate)} {formatLessonResultsDate(resultsEndDate)}
{t('resultsDeadline', { date: formatDate(resultsEndDate, { timeZone: 'Europe/Moscow', wWeekDay: true }) })}
</p>
</div>
{homework && homework.homework.state === 'SENT_FOR_REVIEW' && (
{homework && endDate > new Date() && homework.homework.state === 'SENT_FOR_REVIEW' && (
<div className={classes.resultLinkWrapper}>
<h3 className={classes.resultLinkTitle + ' s-text-28'}>{t('resultLinkTitle')}</h3>
<div className={classes.resultLinkGroup}>
Expand Down Expand Up @@ -95,24 +96,3 @@ function Uppload(props: IUpploadProps) {
</Fragment>
);
}

function getWeekDay(date: Date) {
const weekday = ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'];
return weekday[date.getDay()];
}

function formatLessonDate(date: Date) {
const dateStr = date.toLocaleDateString(
['ru-RU'],
{ month: 'long', day: 'numeric' }
);
return `${dateStr} 2024, `;
}

function formatLessonResultsDate(date: Date) {
const dateStr = date.toLocaleDateString(
['ru-RU'],
{ month: 'long', day: 'numeric' }
);
return `${dateStr}`;
}
7 changes: 5 additions & 2 deletions src/pages/Course/Lesson/LessonHeader/LessonHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Subscription } from 'rxjs';

import { IUserData, userService } from 'services/user.service';
import { formatI18nT } from 'shared';

import useCanShowResults from '../useCanShowResults';
import useFilter from '../useFilter';

import Link from 'ui/Link/Link';
Expand All @@ -25,6 +27,7 @@ function LessonHeader(props: IProps) {
const { lesson, practice } = props;
const { courseId, lessonId } = useParams() as { courseId: string, lessonId: string };
const { filter } = useFilter();
const { canShowResults } = useCanShowResults({ lesson, lessonId, courseId });

const { userId } = filter;
const [user, setUser] = useState<IUserData | null>(null);
Expand All @@ -36,7 +39,7 @@ function LessonHeader(props: IProps) {

let subscription: Subscription;
userService
.getHomeworkBS({ filter: { id: userId }})
.getUserBS({ filter: { id: userId }})
.then(bs => {
subscription = bs.subscribe(action => {
if (action && !(action instanceof Error) && action.users.length) {
Expand All @@ -55,7 +58,7 @@ function LessonHeader(props: IProps) {
<span className='nav-link-arrow'>&rarr;</span>
</Link>
<h1 className={classes.title + ' s-text-56'}>{lesson.title}</h1>
{props.lesson.type === 'Practice' && (
{props.lesson.type === 'Practice' && canShowResults && (
<div className={classes.navTubs}>
<Link
className={classes.type + ' nav-link s-text-18' + (practice === 'task' ? ' isActive' : '')}
Expand Down
6 changes: 5 additions & 1 deletion src/pages/Course/Lesson/LessonUppload/LessonUppload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useCallback, useEffect, useReducer, useRef } from 'react';
import { connect } from 'react-redux';
import { useParams } from 'react-router';

import { homeworkService } from 'services';
import { dataService, homeworkService } from 'services';
import { formatI18nT } from 'shared';

import File from './File/File';
Expand Down Expand Up @@ -254,6 +254,10 @@ function LessonUppload({ homeworkWPopulate, user, scroll, onScrollEnd }: IProps)
}

try {
const lesson = await dataService.lesson.get(state.courseId, state.lessonId);
if (lesson.endDate < new Date()) {
throw new Error('Cannot sent homework past deadline');
}
dispatch({ type: 'PATCH_STATE', payload: { formState: { type: 'pending' }}});
await homeworkService.patchHomework(state.id, { state: 'SENT_FOR_REVIEW' });
dispatch({ type: 'PATCH_STATE', payload: { formState: { type: 'success' }}});
Expand Down
61 changes: 61 additions & 0 deletions src/pages/Course/Lesson/useCanShowResults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useEffect, useState } from 'react';
import type { Subscription } from 'rxjs';

import { formatI18nT } from 'shared';
import { addDays, formatDate } from 'utils';

import { homeworkService } from 'services';
import { userService } from 'services/user.service';

import Fallback from 'ui/Fallback';

import type { ILessonData } from 'types';

const t = formatI18nT('courseLesson');

interface IProps {
lesson: ILessonData | undefined
courseId: string | undefined
lessonId: string | undefined
}

export default function useCanShowResults(props: Readonly<IProps>) {
const { lesson, courseId, lessonId } = props;

const authedUser = userService.useAuthedUser();
const [sentForReviewHomeworksCount, setSentForReviewHomeworksCount] = useState<number | null>(null);
const canShowResults = lesson && lesson.resultsEndDate < new Date() && sentForReviewHomeworksCount === 0
|| authedUser?.role === 'support';

useEffect(() => {
if (!courseId || !lessonId) {
return;
}

let subscription: Subscription;
homeworkService.getHomeworkBS({ filter: { courseId, lessonId, state: 'SENT_FOR_REVIEW' } })
.then(bs => {
subscription = bs.subscribe(e => {
if (e && !(e instanceof Error)) {
setSentForReviewHomeworksCount(e.homeworks.length)
}
});
});

return () => subscription?.unsubscribe();
}, [courseId, lessonId]);

const fallBackType = !lesson ? null:
lesson.resultsEndDate > new Date() ? 'beforeDeadline'
: addDays(lesson.resultsEndDate, 1) > new Date() ? 'deadlineDay'
: 'pastDeadline';
const fallBack = canShowResults || !fallBackType ? null : (
<Fallback.Info>
{t(`fallback:noResults:${fallBackType}`, {
resultsEndDate: formatDate(lesson!.resultsEndDate, { timeZone: 'Europe/Moscow', wWeekDay: true })
})}
</Fallback.Info>
);

return { canShowResults, fallBack };
}
14 changes: 11 additions & 3 deletions src/pages/Course/Lesson/useLessonFallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ import Fallback from 'ui/Fallback';
import { ECommonErrorTypes, type ILessonState } from 'types';
import { i18n } from 'shared';
import { formatDate } from 'utils';
import { IUserData } from 'services/user.service';

interface IProps {
lessonState: ILessonState
authedUser: IUserData | null
}

export default function useLessonFallback(props: Readonly<IProps>) {
const { lessonState, authedUser } = props;

export default function useLessonFallback(lessonState: ILessonState) {
if (lessonState.state) {
if (lessonState.state.type === 'pending') {
return <Fallback.Pending text='loading lessons...'/>;
Expand All @@ -30,8 +38,8 @@ export default function useLessonFallback(lessonState: ILessonState) {
return <Fallback.Error/>;
}

if (lessonState.data.startDate > new Date()) {
const startDate = formatDate(lessonState.data.startDate, { timeZone: 'Europe/Moscow', woTime: true });
if (authedUser?.role !== 'support' && lessonState.data.startDate > new Date()) {
const startDate = formatDate(lessonState.data.startDate, { timeZone: 'Europe/Moscow' });
return <Fallback.Info>{i18n.t('courseLesson.fallback:lessonNotStartedYet', { startDate })}</Fallback.Info>;
}

Expand Down
Loading
Loading