Skip to content

Commit

Permalink
[feat] saturday lessons and some formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
cupoftea4 committed Sep 9, 2024
1 parent 47e6c40 commit 7adcab2
Show file tree
Hide file tree
Showing 11 changed files with 383 additions and 238 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
VITE_PROXY=https://lpnu.pp.ua/get.php?query=
VITE_ENABLE_WORKING_SATURADAYS=true
VITE_FIRST_CLASS_DATE=2024-09-09
2 changes: 2 additions & 0 deletions dev.env
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
VITE_PROXY=https://hope.if.ua/get.php?query=
VITE_ENABLE_WORKING_SATURADAYS=true
VITE_FIRST_CLASS_DATE=2024-09-09
2 changes: 2 additions & 0 deletions hope.env
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
VITE_PROXY=https://hope.if.ua/get.php?query=
VITE_ENABLE_WORKING_SATURADAYS=true
VITE_FIRST_CLASS_DATE=2024-09-09
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "timetable",
"version": "2.6.2",
"version": "2.6.5",
"homepage": "/",
"private": true,
"dependencies": {
Expand Down
181 changes: 110 additions & 71 deletions src/features/timetable/Timetable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type FC, useCallback, useEffect, useMemo, useState } from 'react';
import TimetableLesson from './components/TimetableLesson';
import useWindowDimensions from '@/hooks/useWindowDimensions';
import { lessonsTimes, unique } from '@/utils/timetable';
import { generateSaturdayLessons, lessonsTimes, unique } from '@/utils/timetable';
import { getCurrentUADate, stringToDate } from '@/utils/date';
import { DEVELOP, TIMETABLE_SCREEN_BREAKPOINT } from '@/utils/constants';
import type { TimetableItem } from '@/types/timetable';
Expand All @@ -17,28 +17,45 @@ type OwnProps = {

const MINUTE = 60 * 1000;

const Timetable: FC<OwnProps> = ({ timetable, isSecondSubgroup, isSecondWeek, hasCellSubgroups }) => {
const Timetable: FC<OwnProps> = ({
timetable: originalTimetable,
isSecondSubgroup,
isSecondWeek,
hasCellSubgroups
}) => {
const { width } = useWindowDimensions();
const isMobile = width < TIMETABLE_SCREEN_BREAKPOINT;

const maxLessonNumber = useMemo(() =>
timetable?.reduce((max, item) => item.number > max ? item.number : max, 0) || 0,
[timetable]);
const maxDayNumber = useMemo(() =>
timetable?.reduce((max, item) => item.day > max ? item.day : max, 0) || 0,
[timetable]);
const timetable = useMemo(() => {
return [
...originalTimetable,
...generateSaturdayLessons(originalTimetable)
];
}, [originalTimetable]);

const timetableLessonsTimes = useMemo(() =>
lessonsTimes.slice(0, maxLessonNumber),
[maxLessonNumber]);
const maxLessonNumber = useMemo(
() => timetable?.reduce((max, item) => (item.number > max ? item.number : max), 0) || 0,
[timetable]
);
const maxDayNumber = useMemo(
() => timetable?.reduce((max, item) => (item.day > max ? item.day : max), 0) || 0,
[timetable]
);

const days = useMemo(() =>
[null, 'Понеділок', 'Вівторок', 'Середа', 'Четвер', "П'ятниця", 'Субота', 'Неділя'].slice(0, maxDayNumber + 1),
[maxDayNumber]);
const timetableLessonsTimes = useMemo(() => lessonsTimes.slice(0, maxLessonNumber), [maxLessonNumber]);

const days = useMemo(
() =>
[null, 'Понеділок', 'Вівторок', 'Середа', 'Четвер', "П'ятниця", 'Субота', 'Неділя'].slice(
0,
maxDayNumber + 1
),
[maxDayNumber]
);

const getCurrentLessonNumber = useCallback(() => {
const curDate = getCurrentUADate();
return timetableLessonsTimes.findIndex(time => curDate <= stringToDate(time.end));
return timetableLessonsTimes.findIndex((time) => curDate <= stringToDate(time.end));
}, [timetableLessonsTimes]);

const currentDay = getCurrentUADate().getDay();
Expand All @@ -48,93 +65,115 @@ const Timetable: FC<OwnProps> = ({ timetable, isSecondSubgroup, isSecondWeek, ha
const id = setInterval(() => {
setCurrentLessonNumber(getCurrentLessonNumber());
}, MINUTE);
return () => { clearInterval(id); };
return () => {
clearInterval(id);
};
}, [getCurrentLessonNumber]);

const forBothSubgroups = (item: TimetableItem) => item.isFirstSubgroup && item.isSecondSubgroup;
const forAllWeeks = (item: TimetableItem) => item.isFirstWeek && item.isSecondWeek;

const getLessonsByDayAndTime = useCallback((number: number, day: number) => {
if (!timetable) return null;
const result = timetable.filter(item =>
item.day === day &&
item.number === number &&
(hasCellSubgroups || item.isSecondSubgroup === isSecondSubgroup || forBothSubgroups(item)) &&
(item.isSecondWeek === isSecondWeek || forAllWeeks(item))
);
return result.length === 0 ? null : unique(result);
}, [timetable, isSecondSubgroup, isSecondWeek, hasCellSubgroups]);
const getLessonsByDayAndTime = useCallback(
(number: number, day: number) => {
if (!timetable) return null;
const result = timetable.filter(
(item) =>
item.day === day &&
item.number === number &&
(hasCellSubgroups || item.isSecondSubgroup === isSecondSubgroup || forBothSubgroups(item)) &&
((item.isSecondWeek === isSecondWeek || forAllWeeks(item)) || item.day === 6)
);
return result.length === 0 ? null : unique(result);
},
[timetable, isSecondSubgroup, isSecondWeek, hasCellSubgroups]
);

const tableContent = useMemo(() => {
if (isMobile) return null;
if (DEVELOP) console.log('Running scary useMemo');
const table = timetableLessonsTimes.map((time, i) =>
<tr key={time.start}>{
days.map((day, j) =>
const table = timetableLessonsTimes.map((time, i) => (
<tr key={time.start}>
{days.map((day, j) =>
day === null
? <th key={time.start} style={{ height: '5rem' }}>
<span className={classes(styles.metadata, styles.start)}>{time.start}</span>
<span className={classes(styles.metadata, styles.number)}>{i + 1}</span>
<span className={classes(styles.metadata, styles.end)}>{time.end}</span>
</th>
: <TimetableLesson
isListView={false}
isAfterEmpty={i !== 0 && getLessonsByDayAndTime(i, j) === null}
lessons={getLessonsByDayAndTime(i + 1, j)}
active={currentLessonNumber === i && currentDay === j}
key={time.start + day}
cellSubgroup={hasCellSubgroups}
/>
)
}</tr>
);
? (
<th key={time.start} style={{ height: '5rem' }}>
<span className={classes(styles.metadata, styles.start)}>{time.start}</span>
<span className={classes(styles.metadata, styles.number)}>{i + 1}</span>
<span className={classes(styles.metadata, styles.end)}>{time.end}</span>
</th>
)
: (
<TimetableLesson
isListView={false}
isAfterEmpty={i !== 0 && getLessonsByDayAndTime(i, j) === null}
lessons={getLessonsByDayAndTime(i + 1, j)}
active={currentLessonNumber === i && currentDay === j}
key={time.start + day}
cellSubgroup={hasCellSubgroups}
/>
)
)}
</tr>
));
return table;
}, [
isMobile, timetableLessonsTimes, timetable, days,
getLessonsByDayAndTime, currentLessonNumber, currentDay, hasCellSubgroups
isMobile,
timetableLessonsTimes,
timetable,
days,
getLessonsByDayAndTime,
currentLessonNumber,
currentDay,
hasCellSubgroups
]);

const listsContent = useMemo(() => {
if (!isMobile) return null;
if (DEVELOP) console.log('Running scary useMemo');
const lists = days.filter(Boolean).map((day, i) =>
const lists = days.filter(Boolean).map((day, i) => (
<div key={i} className={styles.list}>
<h3 className={styles['day-title']}>{day}</h3>
<ol className={styles.list}>{
timetableLessonsTimes.map((time, j) =>
<ol className={styles.list}>
{timetableLessonsTimes.map((time, j) => (
<TimetableLesson
isListView={true}
lessons={getLessonsByDayAndTime(j + 1, i + 1)}
active={currentLessonNumber === j && currentDay === i + 1}
key={time.start + day}
cellSubgroup={hasCellSubgroups}
/>
)
}</ol>
))}
</ol>
</div>
);
));
return lists;
}, [
isMobile, timetableLessonsTimes, timetable, days,
getLessonsByDayAndTime, currentLessonNumber, currentDay, hasCellSubgroups
isMobile,
timetableLessonsTimes,
timetable,
days,
getLessonsByDayAndTime,
currentLessonNumber,
currentDay,
hasCellSubgroups
]);

return (
isMobile
? <div className={classes(styles.timetable, styles.lists)}>
{listsContent}
</div>
: <table className={styles.timetable}>
<thead>
<tr>
{days.map((day, index) => <th key={index}>{day}</th>)}
</tr>
</thead>
<tbody>
{tableContent}
</tbody>
</table>
);
return isMobile
? (
<div className={classes(styles.timetable, styles.lists)}>{listsContent}</div>
)
: (
<table className={styles.timetable}>
<thead>
<tr>
{days.map((day, index) => (
<th key={index}>{day}</th>
))}
</tr>
</thead>
<tbody>{tableContent}</tbody>
</table>
);
};

export default Timetable;
2 changes: 1 addition & 1 deletion src/pages/TimetablePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type FC, useEffect, useMemo, useRef, useState } from 'react';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
import LoadingPage from './LoadingPage';
import TimetableHeader from '@/features/header/TimetableHeader';
import ExamsTimetable from '@/features/timetable/ExamsTimetable';
Expand Down
8 changes: 8 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ export const NARROW_SCREEN_BREAKPOINT = 300;
export const TOAST_AUTO_CLOSE_TIME = 5000;

export const DEVELOP = import.meta.env.MODE === 'development';

export const ENABLE_SATURDAYS = import.meta.env.VITE_ENABLE_WORKING_SATURADAYS === 'true';

if (ENABLE_SATURDAYS && !import.meta.env.VITE_FIRST_CLASS_DATE) {
throw new Error('VITE_FIRST_CLASS_DATE is required when ENABLE_WORKING_SATURADAYS is true');
}

export const FIRST_CLASS_DATE = new Date(import.meta.env.VITE_FIRST_CLASS_DATE);
Loading

0 comments on commit 7adcab2

Please sign in to comment.