From 8be0cb25820bf367edbb0bf7aa32e5fe83911f2e Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Tue, 19 Mar 2024 23:22:39 +0900 Subject: [PATCH 1/7] migrate: @/components/tiles/HorizontalTimetableTile.tsx --- .../tiles/HorizontalTimetableTile.tsx | 59 +++++++++++++++++++ src/utils/lectureUtils.ts | 17 ++++++ 2 files changed, 76 insertions(+) create mode 100644 src/components/tiles/HorizontalTimetableTile.tsx create mode 100644 src/utils/lectureUtils.ts diff --git a/src/components/tiles/HorizontalTimetableTile.tsx b/src/components/tiles/HorizontalTimetableTile.tsx new file mode 100644 index 0000000..67f1a63 --- /dev/null +++ b/src/components/tiles/HorizontalTimetableTile.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { withTranslation } from 'react-i18next'; + +import { appBoundClassNames as classNames } from '../../common/boundClassNames'; +import { getProfessorsShortStr } from '../../utils/lectureUtils'; + +import Lecture from '@/shapes/model/subject/Lecture'; +import Classtime from '@/shapes/model/subject/Classtime'; +import { useTranslatedString } from '@/hooks/useTranslatedString'; + +interface Props { + lecture: Lecture; + classtime?: Classtime; + beginIndex: number; + endIndex: number; + color: number; + cellWidth: number; + cellHeight: number; +} + +/** + * 메인페이지의 오늘의 시간표에 보여지는 가로로된 시간표 타일 + */ +const HorizontalTimetableTile: React.FC = ({ + lecture, + classtime, + beginIndex, + endIndex, + color, + cellWidth, + cellHeight, +}) => { + const translate = useTranslatedString(); + + return ( +
+
+

+ {translate(lecture, 'title')} +

+

+ {getProfessorsShortStr(lecture)} +

+

+ {classtime && translate(classtime, 'classroom')} +

+
+
+ ); +}; + +export default withTranslation()(React.memo(HorizontalTimetableTile)); diff --git a/src/utils/lectureUtils.ts b/src/utils/lectureUtils.ts new file mode 100644 index 0000000..53c1ad0 --- /dev/null +++ b/src/utils/lectureUtils.ts @@ -0,0 +1,17 @@ +import { useTranslatedString } from '@/hooks/useTranslatedString'; +import Lecture from '@/shapes/model/subject/Lecture'; +import i18n from 'i18next'; + +// SYNC: Keep synchronized with Django apps/subject/models.py Lecture.get_professors_short_str() +export const getProfessorsShortStr = (lecture: Lecture) => { + const translate = useTranslatedString(); + const professors = lecture.professors.slice().sort((a, b) => (a.name < b.name ? -1 : 1)); + const professorNames = professors.map((p) => translate(p, 'name')); + if (professorNames.length <= 2) { + return professorNames.join(', '); + } + return i18n.t('ui.others.sthAndNumOtherPeople', { + something: professorNames[0], + count: professorNames.length - 1, + }); +}; From 39a2a1f5fd43e6efce88c282265544ee9d529646 Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Wed, 20 Mar 2024 00:10:16 +0900 Subject: [PATCH 2/7] migrate: @/components/tiles/PlannerTile.tsx --- src/components/tiles/PlannerTile.tsx | 179 +++++++++++++++++++++++++++ src/utils/itemUtils.ts | 67 ++++++++++ 2 files changed, 246 insertions(+) create mode 100644 src/components/tiles/PlannerTile.tsx create mode 100644 src/utils/itemUtils.ts diff --git a/src/components/tiles/PlannerTile.tsx b/src/components/tiles/PlannerTile.tsx new file mode 100644 index 0000000..502e45d --- /dev/null +++ b/src/components/tiles/PlannerTile.tsx @@ -0,0 +1,179 @@ +import React from 'react'; +import { withTranslation } from 'react-i18next'; + +import { appBoundClassNames as classNames } from '../../common/boundClassNames'; +import TakenPlannerItem from '@/shapes/model/planner/TakenPlannerItem'; +import FuturePlannerItem from '@/shapes/model/planner/FuturePlannerItem'; +import ArbitraryPlannerItem from '@/shapes/model/planner/ArbitraryPlannerItem'; +import { useTranslatedString } from '@/hooks/useTranslatedString'; +import { getCourseOfItem, getSemesterOfItem } from '@/utils/itemUtils'; +import { PlannerItemType } from '@/shapes/enum'; + +export type ItemType = TakenPlannerItem | FuturePlannerItem | ArbitraryPlannerItem; + +interface Props { + item: ItemType; + yearIndex: number; + semesterIndex: 0 | 1; + beginIndex: number; + endIndex: number; + color: number; + tableSize: number; + cellWidth: number; + cellHeight: number; + isPlannerWithSummer: boolean; + isPlannerWithWinter: boolean; + isDuplicate: boolean; + isRaised: boolean; + isHighlighted: boolean; + isDimmed: boolean; + isSimple: boolean; + onMouseOver?: (item: ItemType) => void; + onMouseOut?: (item: ItemType) => void; + onClick?: (item: ItemType) => void; + deleteLecture: (item: ItemType) => void; +} + +/** + * 졸업플래너의 타일 + */ +const PlannerTile: React.FC = ({ + item, + yearIndex, + semesterIndex, + beginIndex, + endIndex, + color, + tableSize, + cellWidth, + cellHeight, + isPlannerWithSummer, + isDuplicate, + isRaised, + isHighlighted, + isDimmed, + isSimple, + onMouseOver, + onMouseOut, + onClick, + deleteLecture, +}) => { + const translate = useTranslatedString(); + + const handleMouseOver = onMouseOver + ? () => { + onMouseOver(item); + } + : undefined; + const handleMouseOut = onMouseOut + ? () => { + onMouseOut(item); + } + : undefined; + const handleClick = onClick + ? () => { + onClick(item); + } + : undefined; + const handleDeleteFromTableClick = (event: React.MouseEvent) => { + event.stopPropagation(); + deleteLecture(item); + }; + + const getTop = () => { + const base = 17 + (isPlannerWithSummer ? 15 : 0) + cellHeight * tableSize; + if (semesterIndex === 0) { + return base - cellHeight * endIndex + 2; + } + if (semesterIndex === 1) { + return base + cellHeight * 2 + 11 + cellHeight * beginIndex + 1; + } + return base; + }; + + return ( +
+ {item.item_type !== PlannerItemType.TAKEN ? ( + + ) : null} +
+

+ {translate(getCourseOfItem(item), 'title')} +

+ {getSemesterOfItem(item) % 2 === 0 && ( +

+ S +

+ )} + {item.item_type === PlannerItemType.ARBITRARY && ( +

+ ? +

+ )} + {item.is_excluded && ( +

+ X +

+ )} + {isDuplicate && ( +

+ ! +

+ )} +
+
+ ); +}; + +export default withTranslation()(React.memo(PlannerTile)); diff --git a/src/utils/itemUtils.ts b/src/utils/itemUtils.ts new file mode 100644 index 0000000..8f6e60c --- /dev/null +++ b/src/utils/itemUtils.ts @@ -0,0 +1,67 @@ +import { ItemType } from '@/components/tiles/PlannerTile'; +import { PlannerItemType } from '@/shapes/enum'; +import ArbitraryPlannerItem from '@/shapes/model/planner/ArbitraryPlannerItem'; +import Department from '@/shapes/model/subject/Department'; + +export const getSemesterOfItem = (item: ItemType) => { + switch (item.item_type) { + case PlannerItemType.TAKEN: + return item.lecture.semester; + case PlannerItemType.FUTURE: + case PlannerItemType.ARBITRARY: + return item.semester; + default: + return 2000; + } +}; + +export const getIdOfArbitrary = (type: string, typeEn: string, department?: Department) => { + if (department) { + if (typeEn.endsWith('Required')) { + return -(department.id * 100 + 1); + } + if (typeEn.endsWith('Elective')) { + return -(department.id * 100 + 2); + } + return -(department.id * 100 + 3); + } + return -991; +}; + +export const getTitleOfArbitrary = (type: string, typeEn: string, department?: Department) => { + return `임의의 ${type} 과목`; +}; + +export const getTitleEnOfArbitrary = (type: string, typeEn: string, department?: Department) => { + return `Arbitrary ${typeEn} Course`; +}; + +export const getOldCodeOfArbitrary = (type: string, typeEn: string, department?: Department) => { + if (typeEn.startsWith('Major')) { + return `${department && department.code}---`; + } + return 'HSS---'; +}; + +export const getCourseOfItem = (item: ItemType) => { + switch (item.item_type) { + case PlannerItemType.TAKEN: + return item.course; + case PlannerItemType.FUTURE: + return item.course; + case PlannerItemType.ARBITRARY: + default: + return { + id: getIdOfArbitrary(item.type, item.type_en, (item as ArbitraryPlannerItem).department), + isArbitrary: true, + department: item.department, + type: item.type, + type_en: item.type_en, + credit: item.credit, + credit_au: item.credit_au, + title: getTitleOfArbitrary(item.type, item.type_en, item.department), + title_en: getTitleEnOfArbitrary(item.type, item.type_en, item.department), + old_code: getOldCodeOfArbitrary(item.type, item.type_en, item.department), + }; + } +}; From da5cc1e39f506831339d64fea11f6cc17447cdb7 Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Wed, 20 Mar 2024 00:10:59 +0900 Subject: [PATCH 3/7] migrate: @/components/tiles/TimetableDragTile.tsx --- src/components/tiles/TimetableDragTile.tsx | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/components/tiles/TimetableDragTile.tsx diff --git a/src/components/tiles/TimetableDragTile.tsx b/src/components/tiles/TimetableDragTile.tsx new file mode 100644 index 0000000..4cbde1f --- /dev/null +++ b/src/components/tiles/TimetableDragTile.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { withTranslation } from 'react-i18next'; + +import { appBoundClassNames as classNames } from '../../common/boundClassNames'; + +interface Props { + dayIndex: number; + beginIndex: number; + endIndex: number; + cellWidth: number; + cellHeight: number; +} + +/** + * 모의시간표에서 시간대를 드래그 할 때 보여지는 드래그 영역 타일 + */ +const TimetableDragTile: React.FC = ({ + dayIndex, + beginIndex, + endIndex, + cellWidth, + cellHeight, +}) => { + return ( +
+ ); +}; + +export default withTranslation()(React.memo(TimetableDragTile)); From 7436d151628bba91a08695b9fef3913d8866d5ae Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Wed, 20 Mar 2024 00:11:20 +0900 Subject: [PATCH 4/7] migrate: @/components/tiles/TimetableTile.tsx --- .../tiles/HorizontalTimetableTile.tsx | 2 +- src/components/tiles/TimetableTile.tsx | 160 ++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src/components/tiles/TimetableTile.tsx diff --git a/src/components/tiles/HorizontalTimetableTile.tsx b/src/components/tiles/HorizontalTimetableTile.tsx index 67f1a63..6e140dc 100644 --- a/src/components/tiles/HorizontalTimetableTile.tsx +++ b/src/components/tiles/HorizontalTimetableTile.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { withTranslation } from 'react-i18next'; import { appBoundClassNames as classNames } from '../../common/boundClassNames'; -import { getProfessorsShortStr } from '../../utils/lectureUtils'; import Lecture from '@/shapes/model/subject/Lecture'; import Classtime from '@/shapes/model/subject/Classtime'; import { useTranslatedString } from '@/hooks/useTranslatedString'; +import { getProfessorsShortStr } from '@/utils/lectureUtils'; interface Props { lecture: Lecture; diff --git a/src/components/tiles/TimetableTile.tsx b/src/components/tiles/TimetableTile.tsx new file mode 100644 index 0000000..171c54c --- /dev/null +++ b/src/components/tiles/TimetableTile.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import { withTranslation } from 'react-i18next'; + +import { appBoundClassNames as classNames } from '../../common/boundClassNames'; +import { TIMETABLE_START_HOUR, TIMETABLE_END_HOUR } from '../../common/constants'; + +import Lecture from '@/shapes/model/subject/Lecture'; +import Classtime from '@/shapes/model/subject/Classtime'; +import { useTranslatedString } from '@/hooks/useTranslatedString'; +import { getProfessorsShortStr } from '@/utils/lectureUtils'; + +interface Props { + lecture: Lecture; + classtime: Classtime; + tableIndex: number; + dayIndex: number; + beginIndex: number; + endIndex: number; + color: number; + cellWidth: number; + cellHeight: number; + isTimetableReadonly: boolean; + isRaised: boolean; + isHighlighted: boolean; + isDimmed: boolean; + isTemp: boolean; + isSimple: boolean; + onMouseOver?: (lecture: Lecture) => void; + onMouseOut?: (lecture: Lecture) => void; + onClick?: (lecture: Lecture) => void; + deleteLecture: (lecture: Lecture) => void; + occupiedIndices?: number[][]; +} + +/** + * 모의시간표의 시간표 타일 + */ +const TimetableTile: React.FC = ({ + lecture, + classtime, + tableIndex, + dayIndex, + beginIndex, + endIndex, + color, + cellWidth, + cellHeight, + isTimetableReadonly, + isRaised, + isHighlighted, + isDimmed, + isTemp, + isSimple, + onMouseOver, + onMouseOut, + onClick, + deleteLecture, + occupiedIndices, +}) => { + const translate = useTranslatedString(); + + const handleMouseOver = onMouseOver + ? () => { + onMouseOver(lecture); + } + : undefined; + const handleMouseOut = onMouseOut + ? () => { + onMouseOut(lecture); + } + : undefined; + const handleClick = onClick + ? () => { + onClick(lecture); + } + : undefined; + const handleDeleteFromTableClick = (event: React.MouseEvent) => { + event.stopPropagation(); + deleteLecture(lecture); + }; + + const getTop = () => { + if (tableIndex === 0) { + const timedTableOffset = 17 + cellHeight * beginIndex; + return timedTableOffset + 2; + } + const timedTableHeight = 17 + cellHeight * ((TIMETABLE_END_HOUR - TIMETABLE_START_HOUR) * 2); + const untimedTableHeight = 17 + cellHeight * 3; + const tableSpacing = cellHeight; + const untimedTableOffset = 17 + cellHeight * beginIndex; + return ( + timedTableHeight + + untimedTableHeight * (tableIndex - 1) + + tableSpacing * tableIndex + + untimedTableOffset + + 2 + ); + }; + + return ( +
+ {!isTemp && !isTimetableReadonly ? ( + + ) : null} +
onMouseDown()} + className={classNames('tile--timetable__content')}> +

+ {translate(lecture, 'title')} +

+

+ {getProfessorsShortStr(lecture)} +

+

+ {classtime && translate(classtime, 'classroom')} +

+
+ {occupiedIndices === undefined + ? null + : occupiedIndices.map((o) => ( +
+ ))} +
+ ); +}; + +export default withTranslation()(React.memo(TimetableTile)); From 4736ebea68b736396b2718b9ee4d0348e0ee02db Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Wed, 20 Mar 2024 20:43:03 +0900 Subject: [PATCH 5/7] fix: remove jsx files --- .../sections/main/TodaysTimetableSection.jsx | 2 +- .../plannerandinfos/PlannerSubSection.jsx | 2 +- .../timetableandinfos/TimetableSubSection.jsx | 4 +- .../tiles/HorizontalTimetableTile.jsx | 55 ------ src/components/tiles/PlannerTile.jsx | 178 ------------------ src/components/tiles/TimetableDragTile.jsx | 29 --- src/components/tiles/TimetableTile.jsx | 156 --------------- 7 files changed, 4 insertions(+), 422 deletions(-) delete mode 100644 src/components/tiles/HorizontalTimetableTile.jsx delete mode 100644 src/components/tiles/PlannerTile.jsx delete mode 100644 src/components/tiles/TimetableDragTile.jsx delete mode 100644 src/components/tiles/TimetableTile.jsx diff --git a/src/components/sections/main/TodaysTimetableSection.jsx b/src/components/sections/main/TodaysTimetableSection.jsx index 86d3ff4..3c22235 100644 --- a/src/components/sections/main/TodaysTimetableSection.jsx +++ b/src/components/sections/main/TodaysTimetableSection.jsx @@ -10,13 +10,13 @@ import { appBoundClassNames as classNames } from '../../../common/boundClassName import { TIMETABLE_START_HOUR, TIMETABLE_END_HOUR } from '../../../common/constants'; import Scroller from '../../Scroller'; -import HorizontalTimetableTile from '../../tiles/HorizontalTimetableTile'; import userShape from '../../../shapes/model/session/UserShape'; import semesterShape from '../../../shapes/model/subject/SemesterShape'; import { getOngoingSemester } from '../../../utils/semesterUtils'; import { getColorNumber } from '../../../utils/lectureUtils'; +import HorizontalTimetableTile from '@/components/tiles/HorizontalTimetableTile'; class TodaysTimetableSection extends Component { constructor(props) { diff --git a/src/components/sections/planner/plannerandinfos/PlannerSubSection.jsx b/src/components/sections/planner/plannerandinfos/PlannerSubSection.jsx index 6ac7132..55390a3 100644 --- a/src/components/sections/planner/plannerandinfos/PlannerSubSection.jsx +++ b/src/components/sections/planner/plannerandinfos/PlannerSubSection.jsx @@ -29,7 +29,7 @@ import { } from '../../../../utils/itemUtils'; import { getCategoryOfItem, getColorOfItem } from '../../../../utils/itemCategoryUtils'; import { isDimmedItem, isFocusedItem, isTableClickedItem } from '../../../../utils/itemFocusUtils'; -import PlannerTile from '../../../tiles/PlannerTile'; +import PlannerTile from '@/components/tiles/PlannerTile'; import { getSemesterName } from '../../../../utils/semesterUtils'; import PlannerOverlay from '../../../PlannerOverlay'; diff --git a/src/components/sections/timetable/timetableandinfos/TimetableSubSection.jsx b/src/components/sections/timetable/timetableandinfos/TimetableSubSection.jsx index ec97877..9a42f2d 100644 --- a/src/components/sections/timetable/timetableandinfos/TimetableSubSection.jsx +++ b/src/components/sections/timetable/timetableandinfos/TimetableSubSection.jsx @@ -7,7 +7,7 @@ import { range } from 'lodash'; import { appBoundClassNames as classNames } from '../../../../common/boundClassNames'; import { TIMETABLE_START_HOUR, TIMETABLE_END_HOUR } from '../../../../common/constants'; -import TimetableTile from '../../../tiles/TimetableTile'; +import TimetableTile from '@/components/tiles/TimetableTile'; import { setLectureFocus, clearLectureFocus } from '../../../../actions/timetable/lectureFocus'; import { @@ -44,7 +44,7 @@ import { getColorNumber, } from '../../../../utils/lectureUtils'; import { performDeleteFromTable } from '../../../../common/commonOperations'; -import TimetableDragTile from '../../../tiles/TimetableDragTile'; +import TimetableDragTile from '@/components/tiles/TimetableDragTile'; class TimetableSubSection extends Component { constructor(props) { diff --git a/src/components/tiles/HorizontalTimetableTile.jsx b/src/components/tiles/HorizontalTimetableTile.jsx deleted file mode 100644 index 36c0123..0000000 --- a/src/components/tiles/HorizontalTimetableTile.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { withTranslation } from 'react-i18next'; - -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; -import { getProfessorsShortStr } from '../../utils/lectureUtils'; - -import lectureShape from '../../shapes/model/subject/LectureShape'; -import classtimeShape from '../../shapes/model/subject/ClasstimeShape'; - -const HorizontalTimetableTile = ({ - t, - lecture, - classtime, - beginIndex, - endIndex, - color, - cellWidth, - cellHeight, -}) => { - return ( -
-
-

- {lecture[t('js.property.title')]} -

-

- {getProfessorsShortStr(lecture)} -

-

- {classtime[t('js.property.classroom')]} -

-
-
- ); -}; - -HorizontalTimetableTile.propTypes = { - lecture: lectureShape.isRequired, - classtime: classtimeShape, - beginIndex: PropTypes.number.isRequired, - endIndex: PropTypes.number.isRequired, - color: PropTypes.number.isRequired, - cellWidth: PropTypes.number.isRequired, - cellHeight: PropTypes.number.isRequired, -}; - -export default withTranslation()(React.memo(HorizontalTimetableTile)); diff --git a/src/components/tiles/PlannerTile.jsx b/src/components/tiles/PlannerTile.jsx deleted file mode 100644 index fe9fbbd..0000000 --- a/src/components/tiles/PlannerTile.jsx +++ /dev/null @@ -1,178 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { withTranslation } from 'react-i18next'; - -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; - -import takenPlannerItemShape from '../../shapes/model/planner/TakenPlannerItemShape'; -import futurePlannerItemShape from '../../shapes/model/planner/FuturePlannerItemShape'; -import arbitraryPlannerItemShape from '../../shapes/model/planner/ArbitraryPlannerItemShape'; -import { getCourseOfItem, getSemesterOfItem } from '../../utils/itemUtils'; - -const PlannerTile = ({ - t, - item, - yearIndex, - semesterIndex, - beginIndex, - endIndex, - color, - tableSize, - cellWidth, - cellHeight, - isPlannerWithSummer, - isPlannerWithWinter, - isDuplicate, - isRaised, - isHighlighted, - isDimmed, - isSimple, - onMouseOver, - onMouseOut, - onClick, - deleteLecture, -}) => { - const handleMouseOver = onMouseOver - ? (event) => { - onMouseOver(item); - } - : null; - const handleMouseOut = onMouseOut - ? (event) => { - onMouseOut(item); - } - : null; - const handleClick = onClick - ? (event) => { - onClick(item); - } - : null; - const handleDeleteFromTableClick = (event) => { - event.stopPropagation(); - deleteLecture(item); - }; - - const getTop = () => { - const base = 17 + (isPlannerWithSummer ? 15 : 0) + cellHeight * tableSize; - if (semesterIndex === 0) { - return base - cellHeight * endIndex + 2; - } - if (semesterIndex === 1) { - return base + cellHeight * 2 + 11 + cellHeight * beginIndex + 1; - } - return base; - }; - - return ( -
- {item.item_type !== 'TAKEN' ? ( - - ) : null} -
-

- {getCourseOfItem(item)[t('js.property.title')]} -

- {getSemesterOfItem(item) % 2 === 0 && ( -

- S -

- )} - {item.item_type === 'ARBITRARY' && ( -

- ? -

- )} - {item.is_excluded && ( -

- X -

- )} - {isDuplicate && ( -

- ! -

- )} -
-
- ); -}; - -PlannerTile.propTypes = { - item: PropTypes.oneOfType([ - takenPlannerItemShape, - futurePlannerItemShape, - arbitraryPlannerItemShape, - ]).isRequired, - yearIndex: PropTypes.number.isRequired, - semesterIndex: PropTypes.oneOf([0, 1]).isRequired, - beginIndex: PropTypes.number.isRequired, - endIndex: PropTypes.number.isRequired, - color: PropTypes.number.isRequired, - tableSize: PropTypes.number.isRequired, - cellWidth: PropTypes.number.isRequired, - cellHeight: PropTypes.number.isRequired, - isPlannerWithSummer: PropTypes.bool.isRequired, - isPlannerWithWinter: PropTypes.bool.isRequired, - isDuplicate: PropTypes.bool.isRequired, - isRaised: PropTypes.bool.isRequired, - isHighlighted: PropTypes.bool.isRequired, - isDimmed: PropTypes.bool.isRequired, - isSimple: PropTypes.bool.isRequired, - onMouseOver: PropTypes.func, - onMouseOut: PropTypes.func, - onClick: PropTypes.func, - deleteLecture: PropTypes.func.isRequired, -}; - -export default withTranslation()(React.memo(PlannerTile)); diff --git a/src/components/tiles/TimetableDragTile.jsx b/src/components/tiles/TimetableDragTile.jsx deleted file mode 100644 index 9aae46b..0000000 --- a/src/components/tiles/TimetableDragTile.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { withTranslation } from 'react-i18next'; - -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; - -const TimetableDragTile = ({ t, dayIndex, beginIndex, endIndex, cellWidth, cellHeight }) => { - return ( -
- ); -}; - -TimetableDragTile.propTypes = { - dayIndex: PropTypes.number.isRequired, - beginIndex: PropTypes.number.isRequired, - endIndex: PropTypes.number.isRequired, - cellWidth: PropTypes.number.isRequired, - cellHeight: PropTypes.number.isRequired, -}; - -export default withTranslation()(React.memo(TimetableDragTile)); diff --git a/src/components/tiles/TimetableTile.jsx b/src/components/tiles/TimetableTile.jsx deleted file mode 100644 index 465c72f..0000000 --- a/src/components/tiles/TimetableTile.jsx +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { withTranslation } from 'react-i18next'; - -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; -import { TIMETABLE_START_HOUR, TIMETABLE_END_HOUR } from '../../common/constants'; -import { getProfessorsShortStr } from '../../utils/lectureUtils'; - -import lectureShape from '../../shapes/model/subject/LectureShape'; -import classtimeShape from '../../shapes/model/subject/ClasstimeShape'; - -const TimetableTile = ({ - t, - lecture, - classtime, - tableIndex, - dayIndex, - beginIndex, - endIndex, - color, - cellWidth, - cellHeight, - isTimetableReadonly, - isRaised, - isHighlighted, - isDimmed, - isTemp, - isSimple, - onMouseOver, - onMouseOut, - onClick, - deleteLecture, - occupiedIndices, -}) => { - const handleMouseOver = onMouseOver - ? (event) => { - onMouseOver(lecture); - } - : null; - const handleMouseOut = onMouseOut - ? (event) => { - onMouseOut(lecture); - } - : null; - const handleClick = onClick - ? (event) => { - onClick(lecture); - } - : null; - const handleDeleteFromTableClick = (event) => { - event.stopPropagation(); - deleteLecture(lecture); - }; - - const getTop = () => { - if (tableIndex === 0) { - const timedTableOffset = 17 + cellHeight * beginIndex; - return timedTableOffset + 2; - } - const timedTableHeight = 17 + cellHeight * ((TIMETABLE_END_HOUR - TIMETABLE_START_HOUR) * 2); - const untimedTableHeight = 17 + cellHeight * 3; - const tableSpacing = cellHeight; - const untimedTableOffset = 17 + cellHeight * beginIndex; - return ( - timedTableHeight + - untimedTableHeight * (tableIndex - 1) + - tableSpacing * tableIndex + - untimedTableOffset + - 2 - ); - }; - - return ( -
- {!isTemp && !isTimetableReadonly ? ( - - ) : null} -
onMouseDown()} - className={classNames('tile--timetable__content')}> -

- {lecture[t('js.property.title')]} -

-

- {getProfessorsShortStr(lecture)} -

-

- {classtime ? classtime[t('js.property.classroom')] : null} -

-
- {occupiedIndices === undefined - ? null - : occupiedIndices.map((o) => ( -
- ))} -
- ); -}; - -TimetableTile.propTypes = { - lecture: lectureShape.isRequired, - classtime: classtimeShape, - tableIndex: PropTypes.number.isRequired, - dayIndex: PropTypes.number.isRequired, - beginIndex: PropTypes.number.isRequired, - endIndex: PropTypes.number.isRequired, - color: PropTypes.number.isRequired, - cellWidth: PropTypes.number.isRequired, - cellHeight: PropTypes.number.isRequired, - isTimetableReadonly: PropTypes.bool.isRequired, - isRaised: PropTypes.bool.isRequired, - isHighlighted: PropTypes.bool.isRequired, - isDimmed: PropTypes.bool.isRequired, - isTemp: PropTypes.bool.isRequired, - isSimple: PropTypes.bool.isRequired, - onMouseOver: PropTypes.func, - onMouseOut: PropTypes.func, - onClick: PropTypes.func, - deleteLecture: PropTypes.func.isRequired, - occupiedIndices: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), -}; - -export default withTranslation()(React.memo(TimetableTile)); From 48eb362e33db8398acdd37c6bd52021099371a50 Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Wed, 20 Mar 2024 21:34:52 +0900 Subject: [PATCH 6/7] fix: update import and fix typo --- .../tiles/HorizontalTimetableTile.tsx | 17 ++++++++--------- src/components/tiles/PlannerTile.tsx | 3 ++- src/components/tiles/TimetableDragTile.tsx | 2 +- src/components/tiles/TimetableTile.tsx | 5 ++--- src/sass/App.module.scss | 2 +- src/sass/App.module.scss.d.ts | 8 ++++---- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/components/tiles/HorizontalTimetableTile.tsx b/src/components/tiles/HorizontalTimetableTile.tsx index 6e140dc..62b6594 100644 --- a/src/components/tiles/HorizontalTimetableTile.tsx +++ b/src/components/tiles/HorizontalTimetableTile.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { withTranslation } from 'react-i18next'; -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; - -import Lecture from '@/shapes/model/subject/Lecture'; -import Classtime from '@/shapes/model/subject/Classtime'; +import { appBoundClassNames as classNames } from '@/common/boundClassNames'; import { useTranslatedString } from '@/hooks/useTranslatedString'; +import Classtime from '@/shapes/model/subject/Classtime'; +import Lecture from '@/shapes/model/subject/Lecture'; import { getProfessorsShortStr } from '@/utils/lectureUtils'; interface Props { @@ -34,21 +33,21 @@ const HorizontalTimetableTile: React.FC = ({ return (
-
-

+

+

{translate(lecture, 'title')}

-

+

{getProfessorsShortStr(lecture)}

-

+

{classtime && translate(classtime, 'classroom')}

diff --git a/src/components/tiles/PlannerTile.tsx b/src/components/tiles/PlannerTile.tsx index 502e45d..38a28ac 100644 --- a/src/components/tiles/PlannerTile.tsx +++ b/src/components/tiles/PlannerTile.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { withTranslation } from 'react-i18next'; -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; +import { appBoundClassNames as classNames } from '@/common/boundClassNames'; import TakenPlannerItem from '@/shapes/model/planner/TakenPlannerItem'; import FuturePlannerItem from '@/shapes/model/planner/FuturePlannerItem'; import ArbitraryPlannerItem from '@/shapes/model/planner/ArbitraryPlannerItem'; @@ -14,6 +14,7 @@ export type ItemType = TakenPlannerItem | FuturePlannerItem | ArbitraryPlannerIt interface Props { item: ItemType; yearIndex: number; + // semesterIndex: 0 | 1; beginIndex: number; endIndex: number; diff --git a/src/components/tiles/TimetableDragTile.tsx b/src/components/tiles/TimetableDragTile.tsx index 4cbde1f..5eebd76 100644 --- a/src/components/tiles/TimetableDragTile.tsx +++ b/src/components/tiles/TimetableDragTile.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { withTranslation } from 'react-i18next'; -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; +import { appBoundClassNames as classNames } from '@/common/boundClassNames'; interface Props { dayIndex: number; diff --git a/src/components/tiles/TimetableTile.tsx b/src/components/tiles/TimetableTile.tsx index 171c54c..900375d 100644 --- a/src/components/tiles/TimetableTile.tsx +++ b/src/components/tiles/TimetableTile.tsx @@ -1,9 +1,8 @@ import React from 'react'; import { withTranslation } from 'react-i18next'; -import { appBoundClassNames as classNames } from '../../common/boundClassNames'; -import { TIMETABLE_START_HOUR, TIMETABLE_END_HOUR } from '../../common/constants'; - +import { appBoundClassNames as classNames } from '@/common/boundClassNames'; +import { TIMETABLE_START_HOUR, TIMETABLE_END_HOUR } from '@/common/constants'; import Lecture from '@/shapes/model/subject/Lecture'; import Classtime from '@/shapes/model/subject/Classtime'; import { useTranslatedString } from '@/hooks/useTranslatedString'; diff --git a/src/sass/App.module.scss b/src/sass/App.module.scss index 5c6f57b..66533e2 100644 --- a/src/sass/App.module.scss +++ b/src/sass/App.module.scss @@ -1818,7 +1818,7 @@ a { z-index: 1; } - &--horizonatal-timetable { + &--horizontal-timetable { overflow: hidden; &__content { diff --git a/src/sass/App.module.scss.d.ts b/src/sass/App.module.scss.d.ts index eac2782..dc046c2 100644 --- a/src/sass/App.module.scss.d.ts +++ b/src/sass/App.module.scss.d.ts @@ -314,10 +314,10 @@ export declare const textButtonRight: string; export declare const tile: string; export declare const tileDimmed: string; export declare const tileHighlighted: string; -export declare const tileHorizonatalTimetable: string; -export declare const tileHorizonatalTimetableContent: string; -export declare const tileHorizonatalTimetableContentInfo: string; -export declare const tileHorizonatalTimetableContentTitle: string; +export declare const tilehorizontalTimetable: string; +export declare const tilehorizontalTimetableContent: string; +export declare const tilehorizontalTimetableContentInfo: string; +export declare const tilehorizontalTimetableContentTitle: string; export declare const tilePlanner: string; export declare const tilePlannerButton: string; export declare const tilePlannerContent: string; From 364a0f4841df59ea69ec67fa246e0205587bec57 Mon Sep 17 00:00:00 2001 From: YINGMEI Date: Sun, 24 Mar 2024 23:27:57 +0900 Subject: [PATCH 7/7] fix: refactoring code --- .../planner/courselist/CourseListSection.jsx | 18 +-- src/components/tiles/PlannerTile.tsx | 31 ++--- src/components/tiles/TimetableTile.tsx | 42 +++---- src/utils/itemUtils.js | 117 ------------------ src/utils/itemUtils.ts | 67 +++++++++- src/utils/lectureUtils.ts | 17 --- 6 files changed, 95 insertions(+), 197 deletions(-) delete mode 100644 src/utils/itemUtils.js delete mode 100644 src/utils/lectureUtils.ts diff --git a/src/components/sections/planner/courselist/CourseListSection.jsx b/src/components/sections/planner/courselist/CourseListSection.jsx index fb03b91..0dfeabc 100644 --- a/src/components/sections/planner/courselist/CourseListSection.jsx +++ b/src/components/sections/planner/courselist/CourseListSection.jsx @@ -120,9 +120,9 @@ class CourseListSection extends Component { type_en: 'Humanities & Social Elective', credit: 3, credit_au: 0, - title: getTitleOfArbitrary('인문사회선택', 'Humanities & Social Elective', null), - title_en: getTitleEnOfArbitrary('인문사회선택', 'Humanities & Social Elective', null), - old_code: getOldCodeOfArbitrary('인문사회선택', 'Humanities & Social Elective', null), + title: getTitleOfArbitrary('인문사회선택'), + title_en: getTitleEnOfArbitrary('Humanities & Social Elective'), + old_code: getOldCodeOfArbitrary('Humanities & Social Elective', null), }, ]; } @@ -138,9 +138,9 @@ class CourseListSection extends Component { type_en: 'Major Required', credit: 3, credit_au: 0, - title: getTitleOfArbitrary('전공필수', 'Major Required', matchingDepartment), - title_en: getTitleEnOfArbitrary('전공필수', 'Major Required', matchingDepartment), - old_code: getOldCodeOfArbitrary('전공필수', 'Major Required', matchingDepartment), + title: getTitleOfArbitrary('전공필수'), + title_en: getTitleEnOfArbitrary('Major Required'), + old_code: getOldCodeOfArbitrary('Major Required', matchingDepartment), }, { id: getIdOfArbitrary('전공선택', 'Major Elective', matchingDepartment), @@ -150,9 +150,9 @@ class CourseListSection extends Component { type_en: 'Major Elective', credit: 3, credit_au: 0, - title: getTitleOfArbitrary('전공선택', 'Major Elective', matchingDepartment), - title_en: getTitleEnOfArbitrary('전공선택', 'Major Elective', matchingDepartment), - old_code: getOldCodeOfArbitrary('전공선택', 'Major Elective', matchingDepartment), + title: getTitleOfArbitrary('전공선택'), + title_en: getTitleEnOfArbitrary('Major Elective'), + old_code: getOldCodeOfArbitrary('Major Elective', matchingDepartment), }, ]; } diff --git a/src/components/tiles/PlannerTile.tsx b/src/components/tiles/PlannerTile.tsx index 38a28ac..b82ffae 100644 --- a/src/components/tiles/PlannerTile.tsx +++ b/src/components/tiles/PlannerTile.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { withTranslation } from 'react-i18next'; import { appBoundClassNames as classNames } from '@/common/boundClassNames'; -import TakenPlannerItem from '@/shapes/model/planner/TakenPlannerItem'; -import FuturePlannerItem from '@/shapes/model/planner/FuturePlannerItem'; -import ArbitraryPlannerItem from '@/shapes/model/planner/ArbitraryPlannerItem'; import { useTranslatedString } from '@/hooks/useTranslatedString'; -import { getCourseOfItem, getSemesterOfItem } from '@/utils/itemUtils'; import { PlannerItemType } from '@/shapes/enum'; +import ArbitraryPlannerItem from '@/shapes/model/planner/ArbitraryPlannerItem'; +import FuturePlannerItem from '@/shapes/model/planner/FuturePlannerItem'; +import TakenPlannerItem from '@/shapes/model/planner/TakenPlannerItem'; +import { getCourseOfItem, getSemesterOfItem } from '@/utils/itemUtils'; export type ItemType = TakenPlannerItem | FuturePlannerItem | ArbitraryPlannerItem; @@ -61,21 +61,10 @@ const PlannerTile: React.FC = ({ }) => { const translate = useTranslatedString(); - const handleMouseOver = onMouseOver - ? () => { - onMouseOver(item); - } - : undefined; - const handleMouseOut = onMouseOut - ? () => { - onMouseOut(item); - } - : undefined; - const handleClick = onClick - ? () => { - onClick(item); - } - : undefined; + const handleMouseOver = onMouseOver && (() => onMouseOver(item)); + const handleMouseOut = onMouseOut && (() => onMouseOut(item)); + const handleClick = onClick && (() => onClick(item)); + const handleDeleteFromTableClick = (event: React.MouseEvent) => { event.stopPropagation(); deleteLecture(item); @@ -113,13 +102,13 @@ const PlannerTile: React.FC = ({ onMouseOver={handleMouseOver} onMouseOut={handleMouseOut} onClick={handleClick}> - {item.item_type !== PlannerItemType.TAKEN ? ( + {item.item_type !== PlannerItemType.TAKEN && ( - ) : null} + )}

= ({ }) => { const translate = useTranslatedString(); - const handleMouseOver = onMouseOver - ? () => { - onMouseOver(lecture); - } - : undefined; - const handleMouseOut = onMouseOut - ? () => { - onMouseOut(lecture); - } - : undefined; - const handleClick = onClick - ? () => { - onClick(lecture); - } - : undefined; + const handleMouseOver = onMouseOver && (() => onMouseOver(lecture)); + const handleMouseOut = onMouseOut && (() => onMouseOut(lecture)); + const handleClick = onClick && (() => onClick(lecture)); + const handleDeleteFromTableClick = (event: React.MouseEvent) => { event.stopPropagation(); deleteLecture(lecture); @@ -140,18 +129,17 @@ const TimetableTile: React.FC = ({ {classtime && translate(classtime, 'classroom')}

- {occupiedIndices === undefined - ? null - : occupiedIndices.map((o) => ( -
- ))} + {occupiedIndices && + occupiedIndices.map((o) => ( +
+ ))}
); }; diff --git a/src/utils/itemUtils.js b/src/utils/itemUtils.js deleted file mode 100644 index e9872a0..0000000 --- a/src/utils/itemUtils.js +++ /dev/null @@ -1,117 +0,0 @@ -export const getYearOfItem = (item) => { - switch (item.item_type) { - case 'TAKEN': - return item.lecture.year; - case 'FUTURE': - return item.year; - case 'ARBITRARY': - return item.year; - default: - return 2000; - } -}; - -export const getSemesterOfItem = (item) => { - switch (item.item_type) { - case 'TAKEN': - return item.lecture.semester; - case 'FUTURE': - return item.semester; - case 'ARBITRARY': - return item.semester; - default: - return 2000; - } -}; - -export const getCreditOfItem = (item) => { - // TODO: Implement additional customization - if (item.item_type === 'TAKEN') { - return item.lecture.credit; - } - if (item.item_type === 'FUTURE') { - return item.course.credit; - } - if (item.item_type === 'ARBITRARY') { - return item.credit; - } - return 0; -}; - -export const getAuOfItem = (item) => { - if (item.item_type === 'TAKEN') { - return item.lecture.credit_au; - } - if (item.item_type === 'FUTURE') { - return item.course.credit_au; - } - if (item.item_type === 'ARBITRARY') { - return item.credit_au; - } - return 0; -}; - -export const getCreditAndAuOfItem = (item) => { - return getCreditOfItem(item) + getAuOfItem(item); -}; - -export const getIdOfArbitrary = (type, typeEn, department) => { - if (department) { - if (typeEn.endsWith('Required')) { - return -(department.id * 100 + 1); - } - if (typeEn.endsWith('Elective')) { - return -(department.id * 100 + 2); - } - return -(department.id * 100 + 3); - } - return -991; -}; - -export const getTitleOfArbitrary = (type, typeEn, department) => { - return `임의의 ${type} 과목`; -}; - -export const getTitleEnOfArbitrary = (type, typeEn, department) => { - return `Arbitrary ${typeEn} Course`; -}; - -export const getOldCodeOfArbitrary = (type, typeEn, department) => { - if (typeEn.startsWith('Major')) { - return `${department.code}---`; - } - return 'HSS---'; -}; - -export const getCourseOfItem = (item) => { - switch (item.item_type) { - case 'TAKEN': - return item.course; - case 'FUTURE': - return item.course; - case 'ARBITRARY': - return { - id: getIdOfArbitrary(item.type, item.type_en, item.department), - isArbitrary: true, - department: item.department, - type: item.type, - type_en: item.type_en, - credit: item.credit, - credit_au: item.credit_au, - title: getTitleOfArbitrary(item.type, item.type_en, item.department), - title_en: getTitleEnOfArbitrary(item.type, item.type_en, item.department), - old_code: getOldCodeOfArbitrary(item.type, item.type_en, item.department), - }; - default: - return null; - } -}; - -export const isAddedCourse = (course, planner) => { - return ( - planner && - [...planner.taken_items, ...planner.future_items, ...planner.arbitrary_items].some( - (i) => !i.is_excluded && getCourseOfItem(i).id === course.id, - ) - ); -}; diff --git a/src/utils/itemUtils.ts b/src/utils/itemUtils.ts index 8f6e60c..332ebe5 100644 --- a/src/utils/itemUtils.ts +++ b/src/utils/itemUtils.ts @@ -1,8 +1,23 @@ import { ItemType } from '@/components/tiles/PlannerTile'; import { PlannerItemType } from '@/shapes/enum'; import ArbitraryPlannerItem from '@/shapes/model/planner/ArbitraryPlannerItem'; +import Planner from '@/shapes/model/planner/Planner'; +import Course from '@/shapes/model/subject/Course'; import Department from '@/shapes/model/subject/Department'; +export const getYearOfItem = (item: ItemType) => { + switch (item.item_type) { + case PlannerItemType.TAKEN: + return item.lecture.year; + case PlannerItemType.FUTURE: + return item.year; + case PlannerItemType.ARBITRARY: + return item.year; + default: + return 2000; + } +}; + export const getSemesterOfItem = (item: ItemType) => { switch (item.item_type) { case PlannerItemType.TAKEN: @@ -15,6 +30,37 @@ export const getSemesterOfItem = (item: ItemType) => { } }; +export const getCreditOfItem = (item: ItemType) => { + // TODO: Implement additional customization + if (item.item_type === PlannerItemType.TAKEN) { + return item.lecture.credit; + } + if (item.item_type === PlannerItemType.FUTURE) { + return item.course.credit; + } + if (item.item_type === PlannerItemType.ARBITRARY) { + return item.credit; + } + return 0; +}; + +export const getAuOfItem = (item: ItemType) => { + if (item.item_type === PlannerItemType.TAKEN) { + return item.lecture.credit_au; + } + if (item.item_type === PlannerItemType.FUTURE) { + return item.course.credit_au; + } + if (item.item_type === PlannerItemType.ARBITRARY) { + return item.credit_au; + } + return 0; +}; + +export const getCreditAndAuOfItem = (item: ItemType) => { + return getCreditOfItem(item) + getAuOfItem(item); +}; + export const getIdOfArbitrary = (type: string, typeEn: string, department?: Department) => { if (department) { if (typeEn.endsWith('Required')) { @@ -28,15 +74,15 @@ export const getIdOfArbitrary = (type: string, typeEn: string, department?: Depa return -991; }; -export const getTitleOfArbitrary = (type: string, typeEn: string, department?: Department) => { +export const getTitleOfArbitrary = (type: string) => { return `임의의 ${type} 과목`; }; -export const getTitleEnOfArbitrary = (type: string, typeEn: string, department?: Department) => { +export const getTitleEnOfArbitrary = (typeEn: string) => { return `Arbitrary ${typeEn} Course`; }; -export const getOldCodeOfArbitrary = (type: string, typeEn: string, department?: Department) => { +export const getOldCodeOfArbitrary = (typeEn: string, department?: Department) => { if (typeEn.startsWith('Major')) { return `${department && department.code}---`; } @@ -59,9 +105,18 @@ export const getCourseOfItem = (item: ItemType) => { type_en: item.type_en, credit: item.credit, credit_au: item.credit_au, - title: getTitleOfArbitrary(item.type, item.type_en, item.department), - title_en: getTitleEnOfArbitrary(item.type, item.type_en, item.department), - old_code: getOldCodeOfArbitrary(item.type, item.type_en, item.department), + title: getTitleOfArbitrary(item.type), + title_en: getTitleEnOfArbitrary(item.type_en), + old_code: getOldCodeOfArbitrary(item.type_en, item.department), }; } }; + +export const isAddedCourse = (course: Course, planner: Planner) => { + return ( + planner && + [...planner.taken_items, ...planner.future_items, ...planner.arbitrary_items].some( + (i) => !i.is_excluded && getCourseOfItem(i).id === course.id, + ) + ); +}; diff --git a/src/utils/lectureUtils.ts b/src/utils/lectureUtils.ts deleted file mode 100644 index 53c1ad0..0000000 --- a/src/utils/lectureUtils.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useTranslatedString } from '@/hooks/useTranslatedString'; -import Lecture from '@/shapes/model/subject/Lecture'; -import i18n from 'i18next'; - -// SYNC: Keep synchronized with Django apps/subject/models.py Lecture.get_professors_short_str() -export const getProfessorsShortStr = (lecture: Lecture) => { - const translate = useTranslatedString(); - const professors = lecture.professors.slice().sort((a, b) => (a.name < b.name ? -1 : 1)); - const professorNames = professors.map((p) => translate(p, 'name')); - if (professorNames.length <= 2) { - return professorNames.join(', '); - } - return i18n.t('ui.others.sthAndNumOtherPeople', { - something: professorNames[0], - count: professorNames.length - 1, - }); -};