Skip to content

835-fix: Stale data on the website #843

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

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
224911c
fix: 835 - to evaluate is course stale on the client
Quiddlee Apr 6, 2025
0c156e3
feat: 835 - implement to filter stale mentorship courses
Quiddlee Apr 6, 2025
12dc5c8
refactor: 835 - move mentorship stale calculation to the separate method
Quiddlee Apr 6, 2025
fe5f8c4
feat: 835 - implement to evaluate course availability on the client
Quiddlee Apr 7, 2025
80f6eaa
feat: 835 - implement to disable registration link if the course is s…
Quiddlee Apr 7, 2025
fd466c5
fix: 835 - hero course test
Quiddlee Apr 7, 2025
816f2e2
fix: 835 - registration link label when disabled
Quiddlee Apr 7, 2025
c2fd57a
refactor: 835 - remove unnecessary course sorting
Quiddlee Apr 7, 2025
ab878d2
refactor: 835 - move header all courses to the client component
Quiddlee Apr 7, 2025
2de9ce5
fix: 835 - move all static parts to server component
Quiddlee Apr 7, 2025
6413da6
fix: 835 - move all static parts to server component
Quiddlee Apr 7, 2025
5ffacbb
refactor: 835 - encapsulate course menu items in reusable component
Quiddlee Apr 14, 2025
6dc48b6
refactor: 835 - implement to reuse fresh courses component in as many…
Quiddlee Apr 14, 2025
ec6cd2b
refactor: 835 - replace default export with named export
Quiddlee Apr 14, 2025
01d9171
refactor: 835 - remove variable reassignment
Quiddlee Apr 14, 2025
d90c937
refactor: 835 - add explicit types
Quiddlee Apr 14, 2025
9927125
refactor: 835 - rename variable
Quiddlee Apr 14, 2025
cba7bad
chore: 835 - resolve merge conflicts
Quiddlee Apr 19, 2025
c6d3310
chore: 835 - resolve merge conflicts
Quiddlee May 8, 2025
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
53 changes: 51 additions & 2 deletions src/shared/helpers/get-actual-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,34 @@ type GetActualDataParams<T extends DataType> = {
data: T;
staleAfter?: number;
filterStale?: boolean;
isMentorship?: boolean;
sort?: boolean;
};

type GetActualDataType = <T extends DataType>(params: GetActualDataParams<T>) => T;

export const getActualData: GetActualDataType = ({ data, staleAfter, filterStale = true }) => {
export const getActualData: GetActualDataType = ({
data,
staleAfter,
filterStale = true,
isMentorship = false,
sort = true,
}) => {
let dataWithTBD = mapStaleAsTBD(data, staleAfter);

if (isMentorship) {
dataWithTBD = mapMentorshipStaleAsTBD(data);
}

if (filterStale) {
dataWithTBD = filterStaleData(dataWithTBD);
}

return sortData(dataWithTBD);
if (sort) {
dataWithTBD = sortData(dataWithTBD);
}

return dataWithTBD;
};

const mapStaleAsTBD = <T extends DataType>(data: T, staleAfter?: number): T =>
Expand All @@ -45,6 +61,39 @@ const mapStaleAsTBD = <T extends DataType>(data: T, staleAfter?: number): T =>
};
}) as T;

const mapMentorshipStaleAsTBD = <T extends DataType>(data: T): T => {
if ('eventType' in data) {
return data;
}

const mentorshipDataWithTBD = (data as Course[]).map((item) => {
const date: string | null = item.personalMentoringStartDate;

if (!date) {
return item;
}

const daysBeforeStale = dayJS(item.personalMentoringEndDate).diff(
item.personalMentoringStartDate,
'd',
);

const startDate = getCourseDate(date, daysBeforeStale);

if (startDate === TO_BE_DETERMINED) {
return {
...item,
personalMentoringStartDate: null,
personalMentoringEndDate: null,
};
}

return item;
}) as T;

return mentorshipDataWithTBD;
};

const filterStaleData = <T extends DataType>(data: T): T =>
data.filter((item) => {
const date = isCourse(item) ? item.startDate : item.date;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import { Course } from '@/entities/course';
import { FreshCourses } from '@/shared/ui/fresh-courses';
import { SchoolMenu } from '@/widgets/school-menu';
import { Color } from '@/widgets/school-menu/types';

type AllCoursesProps = {
courses: Course[];
icon?: 'iconSmall' | 'iconFooter';
color?: Color;
onClose?: () => void;
};

export const CourseMenuItemsFresh = ({
courses,
icon = 'iconSmall',
color,
onClose,
}: AllCoursesProps) => {
return (
<FreshCourses
courses={courses}
renderCourse={(course) => (
<SchoolMenu.Item
key={course.id}
icon={course[icon]}
title={course.title}
description={course.startDate}
url={course.detailsUrl}
color={color}
onClick={onClose}
/>
)}
/>
);
};
1 change: 1 addition & 0 deletions src/shared/ui/course-menu-items-fresh/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CourseMenuItemsFresh } from './course-menu-items-fresh';
38 changes: 38 additions & 0 deletions src/shared/ui/fresh-courses/fresh-courses.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client';

import { ReactNode } from 'react';

import { Course } from '@/entities/course';
import { getActualData } from '@/shared/helpers/get-actual-data';
import {
transformCoursesToMentorship,
} from '@/views/mentorship/helpers/transform-courses-to-mentorship';

type FreshCoursesProps = {
courses: Course[];
renderCourse: (course: Course) => ReactNode;
filterStale?: boolean;
sort?: boolean;
mentorship?: boolean;
};

export const FreshCourses = ({
courses,
renderCourse,
filterStale = false,
sort = false,
mentorship,
}: FreshCoursesProps) => {
const actualCourses = getActualData({
data: courses,
filterStale,
sort,
isMentorship: mentorship,
});

if (mentorship) {
return transformCoursesToMentorship(actualCourses).map(renderCourse);
}

return actualCourses.map(renderCourse);
};
1 change: 1 addition & 0 deletions src/shared/ui/fresh-courses/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FreshCourses } from './fresh-courses';
28 changes: 28 additions & 0 deletions src/shared/ui/short-info-panel/course-start-label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import { PropsWithChildren } from 'react';

import { calculateFreshDate } from '@/shared/helpers/get-course-date';
import { DateSimple } from '@/shared/ui/date-simple';

type CourseStartLabelProps = PropsWithChildren & {
startDate: string | null;
registrationEndDate: string | null;
label: string | undefined;
};

export const CourseStartLabel = ({
startDate,
registrationEndDate,
label,
children,
}: CourseStartLabelProps) => {
const freshDate =
startDate && registrationEndDate ? calculateFreshDate(startDate, registrationEndDate) : null;

return (
<DateSimple label={label} startDate={freshDate}>
{children}
</DateSimple>
);
};
14 changes: 5 additions & 9 deletions src/shared/ui/short-info-panel/short-info-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import classNames from 'classnames/bind';
import Image from 'next/image';

import { DateSimple } from '../date-simple';
import micIcon from '@/shared/assets/icons/mic.svg';
import { LABELS } from '@/shared/constants';
import { calculateFreshDate } from '@/shared/helpers/get-course-date';
import { Language } from '@/shared/types';
import { CourseStartLabel } from '@/shared/ui/short-info-panel/course-start-label';

import styles from './short-info-panel.module.scss';

Expand Down Expand Up @@ -34,20 +33,17 @@ export const ShortInfoPanel = ({

return (
<section className={cx('info', { margin: withMargin })}>
<DateSimple
<CourseStartLabel
startDate={startDate}
registrationEndDate={registrationEndDate}
label={label}
startDate={
startDate && registrationEndDate
? calculateFreshDate(startDate, registrationEndDate)
: null
}
>
{onlyLanguage && (
<span className={cx('language')} data-testid="course-language">
{courseLanguage}
</span>
)}
</DateSimple>
</CourseStartLabel>
{!onlyLanguage && (
<p className={cx('additional-info')}>
<Image className={cx('icon')} src={micIcon} alt="microphone-icon" />
Expand Down
18 changes: 18 additions & 0 deletions src/widgets/courses/ui/course-items-fresh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client';

import { Course, CourseCard } from '@/entities/course';
import { FreshCourses } from '@/shared/ui/fresh-courses';

type CourseItemsProps = {
courses: Course[];
};

export const CourseItemsFresh = ({ courses }: CourseItemsProps) => {
return (
<FreshCourses
sort
courses={courses}
renderCourse={(course) => <CourseCard size="sm" key={course.id} {...course} />}
/>
);
};
14 changes: 2 additions & 12 deletions src/widgets/courses/ui/courses.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import classNames from 'classnames/bind';

import { type Course, CourseCard } from '@/entities/course';
import { getCourses } from '@/entities/course/api/course-api';
import { getActualData } from '@/shared/helpers/get-actual-data';
import { WidgetTitle } from '@/shared/ui/widget-title';
import { CourseItemsFresh } from '@/widgets/courses/ui/course-items-fresh';

import styles from './courses.module.scss';

Expand All @@ -12,21 +11,12 @@ const cx = classNames.bind(styles);
export const Courses = async () => {
const courses = await getCourses();

const sortParams = {
data: courses,
filterStale: false,
};

const sortedCourses: Course[] = getActualData(sortParams);

return (
<section className={cx('container')} data-testid="all-courses">
<div className={cx('content', 'courses-content')}>
<WidgetTitle>All courses</WidgetTitle>
<div className={cx('courses-list')}>
{sortedCourses.map((course) => {
return <CourseCard size="sm" key={course.id} {...course} />;
})}
<CourseItemsFresh courses={courses} />
</div>
</div>
</section>
Expand Down
12 changes: 2 additions & 10 deletions src/widgets/footer/ui/desktop-view.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AboutList } from './about-list';
import { Course } from '@/entities/course';
import { CourseMenuItemsFresh } from '@/shared/ui/course-menu-items-fresh';
import { SchoolMenu } from '@/widgets/school-menu';
import { schoolMenuStaticLinks } from 'data';

Expand All @@ -26,16 +27,7 @@ export const DesktopView = ({ courses }: DesktopViewProps) => {

<div className="right">
<SchoolMenu heading="all courses" color="light">
{courses.map((course) => (
<SchoolMenu.Item
key={course.id}
icon={course.iconFooter}
title={course.title}
description={course.startDate}
url={course.detailsUrl}
color="light"
/>
))}
<CourseMenuItemsFresh courses={courses} color="light" icon="iconFooter" />
</SchoolMenu>
</div>
</div>
Expand Down
11 changes: 2 additions & 9 deletions src/widgets/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname } from 'next/navigation';
import { BurgerMenu } from './ui/burger/burger';
import { Course } from '@/entities/course';
import { ANCHORS, ROUTES } from '@/shared/constants';
import { CourseMenuItemsFresh } from '@/shared/ui/course-menu-items-fresh';
import { Logo } from '@/shared/ui/logo';
import { NavItem } from '@/widgets/header/ui/nav-item/nav-item';
import { MobileView } from '@/widgets/mobile-view';
Expand Down Expand Up @@ -87,15 +88,7 @@ export const Header = ({ courses }: HeaderProps) => {
</NavItem>
<NavItem label="Courses" href={ROUTES.COURSES}>
<SchoolMenu>
{courses.map((course) => (
<SchoolMenu.Item
key={course.id}
icon={course.iconSmall}
title={course.title}
description={course.startDate}
url={course.detailsUrl}
/>
))}
<CourseMenuItemsFresh courses={courses} />
</SchoolMenu>
</NavItem>
<NavItem label="Community" href={ROUTES.COMMUNITY}>
Expand Down
16 changes: 16 additions & 0 deletions src/widgets/hero-course/ui/availability-status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { dayJS } from '@/shared/helpers/day-js';
import { SectionLabel } from '@/shared/ui/section-label';
import { getCourseStatus } from '@/widgets/hero-course/helpers/get-course-status';

type AvailabilityStatusProps = {
startDate: string;
registrationEndDate: string;
};

export const AvailabilityStatus = ({ startDate, registrationEndDate }: AvailabilityStatusProps) => {
const status = getCourseStatus(startDate, dayJS(registrationEndDate).diff(startDate, 'd'));

return <SectionLabel data-testid="course-label">{status}</SectionLabel>;
};
Loading
Loading