From 37c0ef3e5009e4dfb69d09ca237c7331ce403e6d Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 15 Jul 2020 23:13:19 +0800 Subject: [PATCH 01/48] Add CustomModulesContainer to the TimetableContent --- .../src/views/timetable/TimetableContent.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/website/src/views/timetable/TimetableContent.tsx b/website/src/views/timetable/TimetableContent.tsx index 9e50356664..9a536df814 100644 --- a/website/src/views/timetable/TimetableContent.tsx +++ b/website/src/views/timetable/TimetableContent.tsx @@ -40,6 +40,7 @@ import { } from 'utils/timetables'; import { resetScrollPosition } from 'utils/react'; import ModulesSelectContainer from 'views/timetable/ModulesSelectContainer'; +import CustomModulesContainer from 'views/timetable/CustomModulesContainer'; import Announcements from 'views/components/notfications/Announcements'; import Title from 'views/components/Title'; import ErrorBoundary from 'views/errors/ErrorBoundary'; @@ -411,12 +412,18 @@ class TimetableContent extends React.Component {
{!readOnly && ( - +
+ + +
)}
From 7ce94e02529ba77bce397ebefca6e7f4f470ce37 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 15 Jul 2020 23:14:24 +0800 Subject: [PATCH 02/48] Add styling to CustomModulesContainer' --- .../timetable/CustomModulesContainer.scss | 7 +++ .../timetable/CustomModulesContainer.tsx | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 website/src/views/timetable/CustomModulesContainer.scss create mode 100644 website/src/views/timetable/CustomModulesContainer.tsx diff --git a/website/src/views/timetable/CustomModulesContainer.scss b/website/src/views/timetable/CustomModulesContainer.scss new file mode 100644 index 0000000000..9b8d451e64 --- /dev/null +++ b/website/src/views/timetable/CustomModulesContainer.scss @@ -0,0 +1,7 @@ +@import '~styles/utils/modules-entry'; + +.titleBtn { + @include vertical-mode { + display: none; + } + } \ No newline at end of file diff --git a/website/src/views/timetable/CustomModulesContainer.tsx b/website/src/views/timetable/CustomModulesContainer.tsx new file mode 100644 index 0000000000..ebe513c163 --- /dev/null +++ b/website/src/views/timetable/CustomModulesContainer.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import classnames from 'classnames'; +import { connect } from 'react-redux'; + +import CustomModulesForm from 'views/timetable/CustomModulesForm'; +import styles from './CustomModulesContainer.scss'; + +class CustomModulesAddContainer extends React.Component { + state = { + showAddCustomModulesForm: false, + }; + + onChange = () => { + if (this.state.showAddCustomModulesForm === true) { + this.setState({showAddCustomModulesForm: false }) + } + else { + this.setState({showAddCustomModulesForm: true }) + } + }; + + render() { + return ( + + ); + } +} + +function mapStateToProps() { + + return { + + }; +} + +export default connect(mapStateToProps, {})(CustomModulesAddContainer); From 1e35b4c0a7882df83d4e3223edfd808a2060e78e Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 15 Jul 2020 23:15:07 +0800 Subject: [PATCH 03/48] Add CustomModulesForm to add new customised modules --- website/src/views/timetable/CustomModulesForm.scss | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/src/views/timetable/CustomModulesForm.scss diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss new file mode 100644 index 0000000000..e69de29bb2 From 45d87f27d90a5d13963871a7d109dc921c3bfc3e Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 15 Jul 2020 23:15:19 +0800 Subject: [PATCH 04/48] Add styling to form --- .../src/views/timetable/CustomModulesForm.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 website/src/views/timetable/CustomModulesForm.tsx diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx new file mode 100644 index 0000000000..ad1d6a56cc --- /dev/null +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import classnames from 'classnames'; + +import styles from './CustomModulesForm.scss'; + +class CustomModulesForm extends React.Component<> { + render() { + return (); + } +} + +function mapStateToProps() { + return {}; +} + +export default connect(mapStateToProps, {})(CustomModulesForm); From a6924cc865ec35c83db30d48ad080872f76b5164 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Thu, 16 Jul 2020 02:31:41 +0800 Subject: [PATCH 05/48] Move the form out of the button in CustomModulesContainer' --- .../timetable/CustomModulesContainer.tsx | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/website/src/views/timetable/CustomModulesContainer.tsx b/website/src/views/timetable/CustomModulesContainer.tsx index ebe513c163..5d0edc47a6 100644 --- a/website/src/views/timetable/CustomModulesContainer.tsx +++ b/website/src/views/timetable/CustomModulesContainer.tsx @@ -5,30 +5,36 @@ import { connect } from 'react-redux'; import CustomModulesForm from 'views/timetable/CustomModulesForm'; import styles from './CustomModulesContainer.scss'; -class CustomModulesAddContainer extends React.Component { +type State = { + showCustomModulesForm: boolean, +}; + +class CustomModulesAddContainer extends React.Component { state = { - showAddCustomModulesForm: false, + showCustomModulesForm: false, }; onChange = () => { - if (this.state.showAddCustomModulesForm === true) { - this.setState({showAddCustomModulesForm: false }) + if (this.state.showCustomModulesForm === true) { + this.setState({showCustomModulesForm: false }) } else { - this.setState({showAddCustomModulesForm: true }) + this.setState({showCustomModulesForm: true }) } }; render() { return ( - {this.state.showCustomModulesForm && } - + ); } } From 0ff5e0eddca2740ab466c5ddf5efbbeb70e4c7a1 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Thu, 16 Jul 2020 02:32:08 +0800 Subject: [PATCH 06/48] Add styling to input of CustomModulesForm --- .../views/timetable/CustomModulesForm.scss | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index e69de29bb2..42d687c2f8 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -0,0 +1,23 @@ +@import '~styles/utils/modules-entry'; + +$input-height: 2.75rem; +$module-list-height: 13.5rem; +$item-padding-vertical: 0.6rem; + +.input { + composes: form-control from global; + text-align: left; + white-space: nowrap; + + &:global(.form-control) { + height: $input-height; + } + + &:not(:disabled) { + box-shadow: inset 2px 2px 4px rgba(#000, 0.1); + + @include night-mode { + box-shadow: inset 2px 2px 4px rgba(#000, 0.9); + } + } +} \ No newline at end of file From 371f32d8cb27815bd13bd01aac0498df7757129a Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Thu, 16 Jul 2020 02:32:41 +0800 Subject: [PATCH 07/48] Form to add custom modules and handle on submit function --- .../src/views/timetable/CustomModulesForm.tsx | 152 +++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index ad1d6a56cc..b47acbbe45 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -2,11 +2,159 @@ import * as React from 'react'; import { connect } from 'react-redux'; import classnames from 'classnames'; +import { addModule } from 'actions/timetables'; +import { + Module, + AcadYear, + ModuleCode, + ModuleTitle, + Department, + Faculty, + SemesterData, + Semester, +} from 'types/modules'; import styles from './CustomModulesForm.scss'; -class CustomModulesForm extends React.Component<> { + +type State = { + acadYear: AcadYear; + moduleCode: ModuleCode; + title: ModuleTitle; + department: Department; + faculty: Faculty; + moduleCredit: string; + semesterData: SemesterData[]; + timestamp: number; +}; + + +const semesterOneData = { + semester: 1, + timetable: [ + { + classNo: 'A1', + startTime: '1400', + endTime: '1700', + weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + venue: 'UTSRC-LT51', + day: 'Thursday', + lessonType: 'Sectional Teaching', + size: 20, + }, + ], + examDate: '2018-12-06T13:00:00.000+08:00', + examDuration: 120, +}; + +const semesterTwoData = { + semester: 2, + timetable: [ + { + classNo: 'A1', + startTime: '0900', + endTime: '1200', + weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + venue: 'BIZ2-0510', + day: 'Monday', + lessonType: 'Sectional Teaching', + size: 20, + }, + { + classNo: 'A2', + startTime: '1300', + endTime: '1600', + weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], + venue: 'BIZ2-0510', + day: 'Monday', + lessonType: 'Sectional Teaching', + size: 20, + }, + ], + examDate: '2019-05-09T13:00:00.000+08:00', + examDuration: 120, +}; + +class CustomModulesForm extends React.Component { + state: State = { + acadYear: '', + moduleCode: '', + title: '', + moduleCredit: '', + department: '', + faculty: '', + semesterData: [ + { + semester: 0, + timetable: [ + { + classNo: '', + startTime: '', + endTime: '', + weeks: [], + venue: '', + day: '', + lessonType: '', + }, + ], + }, + ], + timestamp: Date.now(), + }; + + onSubmit = (evt: React.SyntheticEvent) => { + evt.preventDefault(); + let customModule: Module = { + acadYear: this.state.acadYear, + moduleCode: this.state.moduleCode, + title: this.state.title, + moduleCredit: this.state.moduleCredit, + department: this.state.department, + faculty: this.state.faculty, + semesterData: this.state.semesterData, + timestamp: Date.now() || this.state.timestamp, + }; + customModule = { + acadYear: '2018/2019', + description: 'This course aims to help students understand the role of information...', + preclusion: 'Students who have passed FNA1006', + faculty: 'Business', + department: 'Accounting', + title: 'Accounting Information Systems', + workload: [0, 3, 0, 4, 3], + prerequisite: 'FNA1002 or ACC1002', + moduleCredit: '4', + moduleCode: 'CS1010S', + semesterData: [semesterOneData, semesterTwoData], + timestamp: Date.now(), + } + + console.log(customModule) + addModule(this.state.semesterData[0].semester, customModule.moduleCode); + } + render() { - return (); + return ( +
+ this.setState({ moduleCode: e.target.value })} + placeholder="Module Code" + /> + this.setState({ title: e.target.value })} + placeholder="Module Title" + /> + + +
+ ); } } From bc7eced8b025a360f3254a053fbafde4a9399a8a Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Thu, 16 Jul 2020 17:48:26 +0800 Subject: [PATCH 08/48] Remove console log statement --- website/src/actions/timetables.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index f9014f051c..8b2540ad06 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -46,7 +46,6 @@ export function addModule(semester: Semester, moduleCode: ModuleCode) { return (dispatch: Function, getState: GetState) => dispatch(fetchModule(moduleCode)).then(() => { const module: Module = getState().moduleBank.modules[moduleCode]; - if (!module) { dispatch( openNotification(`Cannot load ${moduleCode}`, { From 33c0c0e65a5edb37a0a023780f3c20c2d5628ff4 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Thu, 16 Jul 2020 17:49:25 +0800 Subject: [PATCH 09/48] remove console log statements from actions timetables --- website/src/reducers/timetables.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/website/src/reducers/timetables.ts b/website/src/reducers/timetables.ts index 0c900dd3cd..2fc94d7da2 100644 --- a/website/src/reducers/timetables.ts +++ b/website/src/reducers/timetables.ts @@ -201,7 +201,6 @@ function timetables( case HIDE_LESSON_IN_TIMETABLE: case SHOW_LESSON_IN_TIMETABLE: { const { semester } = action.payload; - return produce(state, (draft) => { draft.lessons[semester] = semTimetable(draft.lessons[semester], action); draft.colors[semester] = semColors(state.colors[semester], action); From 1864adec40e2e98d5e2fa5927ee5b9c55bc2e9b8 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Thu, 16 Jul 2020 17:50:04 +0800 Subject: [PATCH 10/48] Map Dispatch to Props of Custom Modules Form and call AddModule action --- website/src/views/timetable/CustomModulesForm.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index b47acbbe45..1f8ca1348d 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -15,6 +15,9 @@ import { } from 'types/modules'; import styles from './CustomModulesForm.scss'; +type Props = { + addModule: (semester: Semester, moduleCode: ModuleCode) => void; +} type State = { acadYear: AcadYear; @@ -74,7 +77,7 @@ const semesterTwoData = { examDuration: 120, }; -class CustomModulesForm extends React.Component { +class CustomModulesForm extends React.Component { state: State = { acadYear: '', moduleCode: '', @@ -84,7 +87,7 @@ class CustomModulesForm extends React.Component { faculty: '', semesterData: [ { - semester: 0, + semester: 1, timetable: [ { classNo: '', @@ -123,13 +126,11 @@ class CustomModulesForm extends React.Component { workload: [0, 3, 0, 4, 3], prerequisite: 'FNA1002 or ACC1002', moduleCredit: '4', - moduleCode: 'CS1010S', + moduleCode: 'MA1101R', semesterData: [semesterOneData, semesterTwoData], timestamp: Date.now(), } - - console.log(customModule) - addModule(this.state.semesterData[0].semester, customModule.moduleCode); + this.props.addModule(this.state.semesterData[0].semester, customModule.moduleCode); } render() { @@ -162,4 +163,4 @@ function mapStateToProps() { return {}; } -export default connect(mapStateToProps, {})(CustomModulesForm); +export default connect(mapStateToProps, { addModule })(CustomModulesForm); From f3c2b1bbdd0ad207e004de329be4ce859be67194 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Fri, 17 Jul 2020 01:14:57 +0800 Subject: [PATCH 11/48] CustomModules Form to render CustomModuleTimetableForm --- website/src/views/timetable/ModulesSelect.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/views/timetable/ModulesSelect.tsx b/website/src/views/timetable/ModulesSelect.tsx index 62619b63c3..23c872e2b5 100644 --- a/website/src/views/timetable/ModulesSelect.tsx +++ b/website/src/views/timetable/ModulesSelect.tsx @@ -238,6 +238,7 @@ export class ModulesSelectComponent extends React.Component { > {this.props.placeholder} + sfsdsfd Date: Fri, 17 Jul 2020 01:15:27 +0800 Subject: [PATCH 12/48] Add styling for input in CustomMOduleTimetableForm --- .../timetable/CustomModuleTimetableForm.scss | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 website/src/views/timetable/CustomModuleTimetableForm.scss diff --git a/website/src/views/timetable/CustomModuleTimetableForm.scss b/website/src/views/timetable/CustomModuleTimetableForm.scss new file mode 100644 index 0000000000..d37c416e80 --- /dev/null +++ b/website/src/views/timetable/CustomModuleTimetableForm.scss @@ -0,0 +1,59 @@ +@import '~styles/utils/modules-entry'; + +$input-height: 2.75rem; +$module-list-height: 13.5rem; +$item-padding-vertical: 0.6rem; + +.menu { + position: relative; + + .toggle { + width: 100%; + } + + .chevron { + margin: 0 -0.6rem 0 0.2rem; + } + + .dropdownMenu { + width: 16rem; + + svg { + margin-right: 0.2rem; + vertical-align: middle; + } + + @include media-breakpoint-down(xs) { + width: auto; + } + + @include media-breakpoint-up(md) { + font-size: $font-size-sm; + } + + @include media-breakpoint-only(md) { + @include vertical-mode { + right: 0; + left: auto; + } + } + } +} + +.input { + composes: form-control from global; + text-align: left; + white-space: nowrap; + + &:global(.form-control) { + height: $input-height; + } + + &:not(:disabled) { + box-shadow: inset 2px 2px 4px rgba(#000, 0.1); + + @include night-mode { + box-shadow: inset 2px 2px 4px rgba(#000, 0.9); + } + } +} \ No newline at end of file From fe6e1fcbbb82331e3a896565c78775c208d57b62 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Fri, 17 Jul 2020 01:16:01 +0800 Subject: [PATCH 13/48] Add CustomMOduleTimetableForm to inset timetable data through form --- .../timetable/CustomModuleTimetableForm.tsx | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 website/src/views/timetable/CustomModuleTimetableForm.tsx diff --git a/website/src/views/timetable/CustomModuleTimetableForm.tsx b/website/src/views/timetable/CustomModuleTimetableForm.tsx new file mode 100644 index 0000000000..1363cb295c --- /dev/null +++ b/website/src/views/timetable/CustomModuleTimetableForm.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import classnames from 'classnames'; +import { connect } from 'react-redux'; +import Downshift, { ChildrenFunction } from 'downshift'; +import { Counter } from 'utils/react'; + +import styles from './CustomModuleTimetableForm.scss'; + +type OwnProps = { + // Own props + index: number; + onChangeClassNo: (event: React.ChangeEvent, index: number) => void; + onSelectStartTime: (item: string, index: number) => void; + onSelectEndTime: (item: string, index: number) => void; + onSelectVenue: (item: string, index: number) => void; + onSelectDay: (item: string, index: number) => void; + onSelectLessonType: (item: string, index: number) => void; +}; + +type Props = OwnProps; + +type State = { + selectedDay: string; +}; + +export default class CustomModuleTimetableForm extends React.Component { + state: State = { + selectedDay: "" + } + + onSelectDay = (selectedItem: string | null) => { + console.log("SDASD") + if (selectedItem === null) { + console.log("invalid"); + return; + } + const item: string = selectedItem || ""; + this.setState({selectedDay: item}) + console.log(this.state.selectedDay) + this.props.onSelectDay(item, this.props.index) + } + + renderDropdownDay: ChildrenFunction = ({ + isOpen, + getItemProps, + getMenuProps, + toggleMenu, + highlightedIndex, + }) => { + const counter = new Counter(); + return ( +
+ + +
+
+ Monday +
+
+ Tuesday +
+
+
+ ) + } + + render() { + return ( +
+ this.props.onChangeClassNo(e, this.props.index)} + placeholder="Class No." + /> + {this.renderDropdownDay} +
+ ) + } +} \ No newline at end of file From 6131f6c7928cca95a080afe88f3694910ff0c36e Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Fri, 17 Jul 2020 01:16:38 +0800 Subject: [PATCH 14/48] CustomMOdulesForm to render CustomModuleTimetableForm --- .../src/views/timetable/CustomModulesForm.tsx | 140 +++++++++++++----- 1 file changed, 104 insertions(+), 36 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 1f8ca1348d..404ae194ef 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import classnames from 'classnames'; - +import Downshift, { ChildrenFunction } from 'downshift'; import { addModule } from 'actions/timetables'; import { Module, @@ -13,9 +13,17 @@ import { SemesterData, Semester, } from 'types/modules'; +import { Counter } from 'utils/react'; +import { State as StoreState } from 'types/state'; +import CustomModuleTimetableForm from './CustomModuleTimetableForm' import styles from './CustomModulesForm.scss'; -type Props = { +type OwnProps = { + // Own props + activeSemester: Semester; +}; + +type Props = OwnProps & { addModule: (semester: Semester, moduleCode: ModuleCode) => void; } @@ -49,34 +57,6 @@ const semesterOneData = { examDuration: 120, }; -const semesterTwoData = { - semester: 2, - timetable: [ - { - classNo: 'A1', - startTime: '0900', - endTime: '1200', - weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], - venue: 'BIZ2-0510', - day: 'Monday', - lessonType: 'Sectional Teaching', - size: 20, - }, - { - classNo: 'A2', - startTime: '1300', - endTime: '1600', - weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], - venue: 'BIZ2-0510', - day: 'Monday', - lessonType: 'Sectional Teaching', - size: 20, - }, - ], - examDate: '2019-05-09T13:00:00.000+08:00', - examDuration: 120, -}; - class CustomModulesForm extends React.Component { state: State = { acadYear: '', @@ -87,7 +67,7 @@ class CustomModulesForm extends React.Component { faculty: '', semesterData: [ { - semester: 1, + semester: this.props.activeSemester, timetable: [ { classNo: '', @@ -127,11 +107,89 @@ class CustomModulesForm extends React.Component { prerequisite: 'FNA1002 or ACC1002', moduleCredit: '4', moduleCode: 'MA1101R', - semesterData: [semesterOneData, semesterTwoData], + semesterData: [semesterOneData], timestamp: Date.now(), } this.props.addModule(this.state.semesterData[0].semester, customModule.moduleCode); } + + onChangeClassNo = (event: React.ChangeEvent, index: number) => { + this.setState((prevState) => { + return { + semesterData: [{ + semester: prevState.semesterData[0].semester, + timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => + currentIndex === index ? { ...props, classNo: event.target.value } : props + ) + }] + } + }) + }; + + onSelectStartTime = (item: string, index: number) => { + this.setState((prevState) => { + return { + semesterData: [{ + semester: prevState.semesterData[0].semester, + timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => + currentIndex === index ? { ...props, startTime: item } : props + ) + }] + } + }) + }; + + onSelectEndTime = (item: string, index: number) => { + this.setState((prevState) => { + return { + semesterData: [{ + semester: prevState.semesterData[0].semester, + timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => + currentIndex === index ? { ...props, endTime: item } : props + ) + }] + } + }) + }; + + onSelectVenue = (item: string, index: number) => { + this.setState((prevState) => { + return { + semesterData: [{ + semester: prevState.semesterData[0].semester, + timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => + currentIndex === index ? { ...props, venue: item } : props + ) + }] + } + }) + }; + + onSelectDay = (item: string, index: number) => { + this.setState((prevState) => { + return { + semesterData: [{ + semester: prevState.semesterData[0].semester, + timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => + currentIndex === index ? { ...props, day: item } : props + ) + }] + } + }) + }; + + onSelectLessonType = (item: string, index: number) => { + this.setState((prevState) => { + return { + semesterData: [{ + semester: prevState.semesterData[0].semester, + timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => + currentIndex === index ? { ...props, lessonType: item } : props + ) + }] + } + }) + }; render() { return ( @@ -146,6 +204,15 @@ class CustomModulesForm extends React.Component { onChange={e => this.setState({ title: e.target.value })} placeholder="Module Title" /> + - sfsdsfd Date: Sat, 18 Jul 2020 01:14:04 +0800 Subject: [PATCH 17/48] Working downshift element to chosse from Dropdown --- .../timetable/CustomModuleTimetableForm.tsx | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/website/src/views/timetable/CustomModuleTimetableForm.tsx b/website/src/views/timetable/CustomModuleTimetableForm.tsx index 1363cb295c..60247ca4eb 100644 --- a/website/src/views/timetable/CustomModuleTimetableForm.tsx +++ b/website/src/views/timetable/CustomModuleTimetableForm.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import classnames from 'classnames'; import { connect } from 'react-redux'; import Downshift, { ChildrenFunction } from 'downshift'; +import { Days } from 'types/modules'; import { Counter } from 'utils/react'; import styles from './CustomModuleTimetableForm.scss'; @@ -29,23 +30,19 @@ export default class CustomModuleTimetableForm extends React.Component { - console.log("SDASD") if (selectedItem === null) { console.log("invalid"); return; } const item: string = selectedItem || ""; this.setState({selectedDay: item}) - console.log(this.state.selectedDay) this.props.onSelectDay(item, this.props.index) } renderDropdownDay: ChildrenFunction = ({ isOpen, getItemProps, - getMenuProps, toggleMenu, - highlightedIndex, }) => { const counter = new Counter(); return ( @@ -57,26 +54,18 @@ export default class CustomModuleTimetableForm extends React.Component {this.state.selectedDay!==""? this.state.selectedDay : "Day"} - -
- Monday -
-
( +
- Tuesday + {item} +
+ ))}
-
) } @@ -89,7 +78,7 @@ export default class CustomModuleTimetableForm extends React.Component this.props.onChangeClassNo(e, this.props.index)} placeholder="Class No." /> - {this.renderDropdownDay} + {this.renderDropdownDay} ) } From 6ab4260b527c77f4ac02cde358468a97f33a44ba Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 18 Jul 2020 01:14:41 +0800 Subject: [PATCH 18/48] Add styling to adding timetable --- .../views/timetable/CustomModulesForm.scss | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index 42d687c2f8..cf1216aeac 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -3,11 +3,18 @@ $input-height: 2.75rem; $module-list-height: 13.5rem; $item-padding-vertical: 0.6rem; +$btn-margin: 0.5rem; + + +.titleIcon { + composes: svg svg-small from global; +} .input { composes: form-control from global; text-align: left; white-space: nowrap; + display: inline-block; &:global(.form-control) { height: $input-height; @@ -20,4 +27,24 @@ $item-padding-vertical: 0.6rem; box-shadow: inset 2px 2px 4px rgba(#000, 0.9); } } +} + +.buttonGroup { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-left: -$btn-margin; // Negate childrens' left margins + + > * { + flex-grow: 1; + margin-bottom: $btn-margin; + margin-left: $btn-margin; + } + + @include media-breakpoint-up(md) { + :global(.btn) { + padding: 0.25rem 0.75rem; + font-size: $font-size-sm; + } + } } \ No newline at end of file From 29caa3a7e6182f6a7f3e409d3cd79e92f3c117ba Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 18 Jul 2020 01:15:10 +0800 Subject: [PATCH 19/48] CustomModulesForm declare callback function to set properties of the custom modules when timetable is set --- .../src/views/timetable/CustomModulesForm.tsx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 404ae194ef..02285d651c 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { connect } from 'react-redux'; import classnames from 'classnames'; -import Downshift, { ChildrenFunction } from 'downshift'; import { addModule } from 'actions/timetables'; import { Module, @@ -13,7 +12,6 @@ import { SemesterData, Semester, } from 'types/modules'; -import { Counter } from 'utils/react'; import { State as StoreState } from 'types/state'; import CustomModuleTimetableForm from './CustomModuleTimetableForm' import styles from './CustomModulesForm.scss'; @@ -35,6 +33,7 @@ type State = { faculty: Faculty; moduleCredit: string; semesterData: SemesterData[]; + currentTimetableIndex: number; timestamp: number; }; @@ -81,6 +80,7 @@ class CustomModulesForm extends React.Component { ], }, ], + currentTimetableIndex: 0, timestamp: Date.now(), }; @@ -110,6 +110,7 @@ class CustomModulesForm extends React.Component { semesterData: [semesterOneData], timestamp: Date.now(), } + this.state.currentTimetableIndex += 1; this.props.addModule(this.state.semesterData[0].semester, customModule.moduleCode); } @@ -191,21 +192,32 @@ class CustomModulesForm extends React.Component { }) }; + isFormValid = () => { + let validity: boolean = Boolean(this.state.moduleCode) && Boolean(this.state.title); + this.state.semesterData[0].timetable.forEach(function (schedule) { + validity = validity && Boolean(schedule.classNo && schedule.startTime && schedule.endTime && schedule.venue && schedule.day && schedule.lessonType) + }); + console.log(validity); + return validity; + } + render() { return (
+
this.setState({ moduleCode: e.target.value })} placeholder="Module Code" /> this.setState({ title: e.target.value })} placeholder="Module Title" - /> + /> +
{ /> +
+ {Object.keys(LESSON_TYPE_ABBREV).map((item, index) => ( +
+ {item} +
+ ))} +
+
+ ); } onSelectDay = (selectedItem: string | null) => { @@ -35,7 +68,6 @@ export default class CustomModuleTimetableForm extends React.Component { - const counter = new Counter(); return ( -
+
-
+
{Days.map((item, index) => ( -
- {item} -
+
+ {item} +
))} +
+
+ ); + } + + onSelectStartTimeHH = (selectedItem: string | null) => { + const item: string = selectedItem || ""; + this.setState({selectedStartTimeHH: item}) + this.props.onSelectStartTime(item, "HH", this.props.index) + } + + onSelectStartTimeMM = (selectedItem: string | null) => { + const item: string = selectedItem || "00"; + this.setState({selectedStartTimeHH: item}) + this.props.onSelectStartTime(item, "MM", this.props.index) + } + + renderDropdownStartTimeHH: ChildrenFunction = ({ + isOpen, + getItemProps, + toggleMenu, + }) => { + return ( +
+
- ) + ); } + + renderDropdownStartTimeMM: ChildrenFunction = ({ + isOpen, + getItemProps, + toggleMenu, + }) => { + return ( +
+ +
+ ); + }; + + onSelectEndTimeHH = (selectedItem: string | null) => { + const item: string = selectedItem || "00"; + this.setState({selectedStartTimeHH: item}) + this.props.onSelectStartTime(item, "HH", this.props.index) + } + + onSelectEndTimeMM = (selectedItem: string | null) => { + const item: string = selectedItem || "00"; + this.setState({ selectedStartTimeHH: item }) + this.props.onSelectEndTime(item, "MM", this.props.index) + } + render() { return ( -
- this.props.onChangeClassNo(e, this.props.index)} - placeholder="Class No." - /> - {this.renderDropdownDay} +
+
+ this.props.onChangeClassNo(e, this.props.index)} + placeholder="Class No." + /> + {this.renderDropdownLessonType} + this.props.onChangeClassNo(e, this.props.index)} + placeholder="Venue." + /> + {this.renderDropdownDay} + +
+ + {this.renderDropdownStartTimeHH} + + : + + {this.renderDropdownStartTimeMM} + +
+
+ + {this.renderDropdownStartTimeHH} + + : + + {this.renderDropdownStartTimeMM} + +
+
- ) + ); } } \ No newline at end of file From 7c18a50bb4f66e2b322ed5e43a199a9b99f8312e Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 25 Jul 2020 01:03:39 +0800 Subject: [PATCH 22/48] Add margin to the button --- .../src/views/timetable/CustomModulesContainer.scss | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/website/src/views/timetable/CustomModulesContainer.scss b/website/src/views/timetable/CustomModulesContainer.scss index 9b8d451e64..349df8bf1d 100644 --- a/website/src/views/timetable/CustomModulesContainer.scss +++ b/website/src/views/timetable/CustomModulesContainer.scss @@ -1,7 +1,11 @@ @import '~styles/utils/modules-entry'; .titleBtn { - @include vertical-mode { - display: none; - } - } \ No newline at end of file + + margin-bottom: 0.5rem; + margin-top: 0.5rem; + + @include vertical-mode { + display: none; + } +} \ No newline at end of file From 0327ae3fecf214232eccae3c8377c614710864e3 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 25 Jul 2020 01:04:12 +0800 Subject: [PATCH 23/48] Responsive width of the form, using flex basis --- .../views/timetable/CustomModulesForm.scss | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index cf1216aeac..13022e9b78 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -1,3 +1,4 @@ +@import "./_variables.scss"; @import '~styles/utils/modules-entry'; $input-height: 2.75rem; @@ -41,10 +42,39 @@ $btn-margin: 0.5rem; margin-left: $btn-margin; } - @include media-breakpoint-up(md) { + @include media-breakpoint-down(xs) { + > * { + flex: 1 1 100%; + } + } + + @include media-breakpoint-up(sm) { :global(.btn) { padding: 0.25rem 0.75rem; font-size: $font-size-sm; } } +} + + +// Two layout forms: +// +// 1. Left and right on one row, with space between two groups +// - md and above for horizontal +// +// 2. Left and right on 2 rows, full width (fullWidthGroup) +// - any size in vertical +// - sm and below for horizontal +@mixin fullWidthGroup { + .buttonGroup { + width: calc(100% + #{$btn-margin}); // Make full width and negate the left shifting caused by our margin-left + } +} + +:global(.verticalMode) { + @include fullWidthGroup; +} + +@include media-breakpoint-down(sm) { + @include fullWidthGroup; } \ No newline at end of file From 343fbd34d8042d91037e7f70999b12883011f563 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 25 Jul 2020 01:05:03 +0800 Subject: [PATCH 24/48] Add function to handle change on form --- .../src/views/timetable/CustomModulesForm.tsx | 136 ++++++------------ 1 file changed, 40 insertions(+), 96 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 02285d651c..9a0b37aa95 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -9,6 +9,7 @@ import { ModuleTitle, Department, Faculty, + Weeks, SemesterData, Semester, } from 'types/modules'; @@ -32,7 +33,14 @@ type State = { department: Department; faculty: Faculty; moduleCredit: string; - semesterData: SemesterData[]; + semester: Semester; + classNo: string; + startTime: string; + endTime: string; + weeks: Weeks; + venue: string; + day: string; + lessonType: string; currentTimetableIndex: number; timestamp: number; }; @@ -64,22 +72,14 @@ class CustomModulesForm extends React.Component { moduleCredit: '', department: '', faculty: '', - semesterData: [ - { - semester: this.props.activeSemester, - timetable: [ - { - classNo: '', - startTime: '', - endTime: '', - weeks: [], - venue: '', - day: '', - lessonType: '', - }, - ], - }, - ], + semester: this.props.activeSemester, + classNo: '', + startTime: '0000', + endTime: '0000', + weeks: [], + venue: '', + day: '', + lessonType: '', currentTimetableIndex: 0, timestamp: Date.now(), }; @@ -93,8 +93,8 @@ class CustomModulesForm extends React.Component { moduleCredit: this.state.moduleCredit, department: this.state.department, faculty: this.state.faculty, - semesterData: this.state.semesterData, timestamp: Date.now() || this.state.timestamp, + semesterData: [] }; customModule = { acadYear: '2018/2019', @@ -111,109 +111,51 @@ class CustomModulesForm extends React.Component { timestamp: Date.now(), } this.state.currentTimetableIndex += 1; - this.props.addModule(this.state.semesterData[0].semester, customModule.moduleCode); + this.props.addModule(1, customModule.moduleCode); } onChangeClassNo = (event: React.ChangeEvent, index: number) => { - this.setState((prevState) => { - return { - semesterData: [{ - semester: prevState.semesterData[0].semester, - timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => - currentIndex === index ? { ...props, classNo: event.target.value } : props - ) - }] - } - }) + this.setState({classNo : event.target.value}) }; - onSelectStartTime = (item: string, index: number) => { - this.setState((prevState) => { - return { - semesterData: [{ - semester: prevState.semesterData[0].semester, - timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => - currentIndex === index ? { ...props, startTime: item } : props - ) - }] - } - }) + onSelectStartTime = (item: string, type: string, index: number) => { + this.setState(prevState => ({startTime: (type === "HH"? (item + prevState.startTime.slice(-2, 0)) : (prevState.startTime.slice(0, 2) + item))})) }; - onSelectEndTime = (item: string, index: number) => { - this.setState((prevState) => { - return { - semesterData: [{ - semester: prevState.semesterData[0].semester, - timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => - currentIndex === index ? { ...props, endTime: item } : props - ) - }] - } - }) + onSelectEndTime = (item: string, type: string, index: number) => { + this.setState(prevState => ({endTime: (type === "MM"? (item + prevState.startTime.slice(-2, 0)) : (prevState.startTime.slice(0, 2) + item))})) }; onSelectVenue = (item: string, index: number) => { - this.setState((prevState) => { - return { - semesterData: [{ - semester: prevState.semesterData[0].semester, - timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => - currentIndex === index ? { ...props, venue: item } : props - ) - }] - } - }) + this.setState( {venue : item}) }; onSelectDay = (item: string, index: number) => { - this.setState((prevState) => { - return { - semesterData: [{ - semester: prevState.semesterData[0].semester, - timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => - currentIndex === index ? { ...props, day: item } : props - ) - }] - } - }) + this.setState({ day : item }) }; onSelectLessonType = (item: string, index: number) => { - this.setState((prevState) => { - return { - semesterData: [{ - semester: prevState.semesterData[0].semester, - timetable: prevState.semesterData[0].timetable.map((props, currentIndex) => - currentIndex === index ? { ...props, lessonType: item } : props - ) - }] - } - }) + this.setState({ lessonType : item }) }; isFormValid = () => { - let validity: boolean = Boolean(this.state.moduleCode) && Boolean(this.state.title); - this.state.semesterData[0].timetable.forEach(function (schedule) { - validity = validity && Boolean(schedule.classNo && schedule.startTime && schedule.endTime && schedule.venue && schedule.day && schedule.lessonType) - }); - console.log(validity); + const validity = Boolean(this.state.moduleCode && this.state.title && this.state.classNo && this.state.startTime && this.state.endTime && this.state.venue && this.state.day && this.state.lessonType) return validity; } render() { return (
-
- this.setState({ moduleCode: e.target.value })} - placeholder="Module Code" - /> - this.setState({ title: e.target.value })} - placeholder="Module Title" +
+ this.setState({ moduleCode: e.target.value })} + placeholder="Module Code" + /> + this.setState({ title: e.target.value })} + placeholder="Module Title" />
{ > Create Module + {this.state.weeks} + {this.state.semester}
); From e042896213f79d6b99df1584d64eb798233b65a4 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 25 Jul 2020 01:22:22 +0800 Subject: [PATCH 25/48] Make downshift reflect selected value --- .../timetable/CustomModuleTimetableForm.scss | 4 ++-- .../timetable/CustomModuleTimetableForm.tsx | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/website/src/views/timetable/CustomModuleTimetableForm.scss b/website/src/views/timetable/CustomModuleTimetableForm.scss index bfa74cebbe..c70b95a429 100644 --- a/website/src/views/timetable/CustomModuleTimetableForm.scss +++ b/website/src/views/timetable/CustomModuleTimetableForm.scss @@ -89,7 +89,7 @@ $btn-margin: 0.5rem; } .dropdownMenu { - width: 12rem; + width: 100%; svg { margin-right: 0.2rem; @@ -115,7 +115,7 @@ $btn-margin: 0.5rem; } .dropdownMenu { - width: 3rem; + width: 100%; svg { margin-right: 0.2rem; diff --git a/website/src/views/timetable/CustomModuleTimetableForm.tsx b/website/src/views/timetable/CustomModuleTimetableForm.tsx index 537b12e18c..2fde3fec8a 100644 --- a/website/src/views/timetable/CustomModuleTimetableForm.tsx +++ b/website/src/views/timetable/CustomModuleTimetableForm.tsx @@ -33,8 +33,8 @@ export default class CustomModuleTimetableForm extends React.Component = ({ @@ -49,7 +49,7 @@ export default class CustomModuleTimetableForm extends React.Component toggleMenu()} > - {this.state.selectedDay !== '' ? this.state.selectedLessonType : 'Lesson Type'} + {this.state.selectedLessonType !== '' ? this.state.selectedLessonType : 'Lesson Type'}
{Object.keys(LESSON_TYPE_ABBREV).map((item, index) => ( @@ -62,12 +62,23 @@ export default class CustomModuleTimetableForm extends React.Component { + if (selectedItem === null) { + console.log("invalid"); + return; + } + const item: string = selectedItem || ""; + this.setState({ selectedLessonType: item }); + this.props.onSelectLessonType(item, this.props.index) + } + onSelectDay = (selectedItem: string | null) => { if (selectedItem === null) { console.log("invalid"); return; } const item: string = selectedItem || ""; + this.setState({ selectedDay: item }); this.props.onSelectDay(item, this.props.index) } @@ -104,7 +115,7 @@ export default class CustomModuleTimetableForm extends React.Component { const item: string = selectedItem || "00"; - this.setState({selectedStartTimeHH: item}) + this.setState({selectedStartTimeMM: item}) this.props.onSelectStartTime(item, "MM", this.props.index) } @@ -192,7 +203,7 @@ export default class CustomModuleTimetableForm extends React.Component this.props.onChangeClassNo(e, this.props.index)} placeholder="Class No." /> - {this.renderDropdownLessonType} + {this.renderDropdownLessonType} this.props.onChangeClassNo(e, this.props.index)} @@ -209,6 +220,7 @@ export default class CustomModuleTimetableForm extends React.Component
+
{this.renderDropdownStartTimeHH} From 37fa523ba98e81d8dbca02385677be25afed272f Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sat, 25 Jul 2020 16:44:41 +0800 Subject: [PATCH 26/48] Fix bug in handler functions for change in form input --- .../timetable/CustomModuleTimetableForm.tsx | 263 ++++++++++++++---- .../src/views/timetable/CustomModulesForm.tsx | 72 +++-- 2 files changed, 250 insertions(+), 85 deletions(-) diff --git a/website/src/views/timetable/CustomModuleTimetableForm.tsx b/website/src/views/timetable/CustomModuleTimetableForm.tsx index 2fde3fec8a..52bbb976ac 100644 --- a/website/src/views/timetable/CustomModuleTimetableForm.tsx +++ b/website/src/views/timetable/CustomModuleTimetableForm.tsx @@ -12,12 +12,12 @@ import styles from './CustomModuleTimetableForm.scss'; type OwnProps = { // Own props index: number; - onChangeClassNo: (event: React.ChangeEvent, index: number) => void; - onSelectStartTime: (item: string, type: string, index: number) => void; - onSelectEndTime: (item: string, type: string, index: number) => void; - onSelectVenue: (item: string, index: number) => void; - onSelectDay: (item: string, index: number) => void; - onSelectLessonType: (item: string, index: number) => void; + onChangeClassNo: (event: React.ChangeEvent) => void; + onSelectStartTime: (item: string, type: string) => void; + onSelectEndTime: (item: string, type: string) => void; + onChangeVenue: (event: React.ChangeEvent) => void; + onSelectDay: (item: string) => void; + onSelectLessonType: (item: string) => void; }; type Props = OwnProps; @@ -27,6 +27,8 @@ type State = { selectedDay: string, selectedStartTimeHH: string, selectedStartTimeMM: string, + selectedEndTimeHH: string, + selectedEndTimeMM: string, } export default class CustomModuleTimetableForm extends React.Component { @@ -35,7 +37,9 @@ export default class CustomModuleTimetableForm extends React.Component = ({ isOpen, @@ -49,38 +53,47 @@ export default class CustomModuleTimetableForm extends React.Component toggleMenu()} > - {this.state.selectedLessonType !== '' ? this.state.selectedLessonType : 'Lesson Type'} + {this.state.selectedLessonType !== '' + ? this.state.selectedLessonType + : 'Lesson Type'} -
+
{Object.keys(LESSON_TYPE_ABBREV).map((item, index) => ( -
+
{item}
))}
); - } + }; onSelectLessonType = (selectedItem: string | null) => { if (selectedItem === null) { - console.log("invalid"); + console.log('invalid'); return; } - const item: string = selectedItem || ""; + const item: string = selectedItem || ''; this.setState({ selectedLessonType: item }); - this.props.onSelectLessonType(item, this.props.index) - } + this.props.onSelectLessonType(item); + }; onSelectDay = (selectedItem: string | null) => { if (selectedItem === null) { - console.log("invalid"); + console.log('invalid'); return; } - const item: string = selectedItem || ""; + const item: string = selectedItem || ''; this.setState({ selectedDay: item }); - this.props.onSelectDay(item, this.props.index) - } + this.props.onSelectDay(item); + }; renderDropdownDay: ChildrenFunction = ({ isOpen, @@ -96,28 +109,35 @@ export default class CustomModuleTimetableForm extends React.Component {this.state.selectedDay !== '' ? this.state.selectedDay : 'Day'} -
+
{Days.map((item, index) => ( -
+
{item}
))}
); - } + }; onSelectStartTimeHH = (selectedItem: string | null) => { - const item: string = selectedItem || ""; - this.setState({selectedStartTimeHH: item}) - this.props.onSelectStartTime(item, "HH", this.props.index) - } + const item: string = selectedItem || ''; + this.setState({ selectedStartTimeHH: item }); + this.props.onSelectStartTime(item, 'HH'); + }; onSelectStartTimeMM = (selectedItem: string | null) => { - const item: string = selectedItem || "00"; - this.setState({selectedStartTimeMM: item}) - this.props.onSelectStartTime(item, "MM", this.props.index) - } + const item: string = selectedItem || '00'; + this.setState({ selectedStartTimeMM: item }); + this.props.onSelectStartTime(item, 'MM'); + }; renderDropdownStartTimeHH: ChildrenFunction = ({ isOpen, @@ -126,9 +146,19 @@ export default class CustomModuleTimetableForm extends React.Component { return (
- +
+ ); + }; + + renderDropdownEndTimeHH: ChildrenFunction = ({ + isOpen, + getItemProps, + toggleMenu, + }) => { + return ( +
+ +
+ ); + }; + + renderDropdownEndTimeMM: ChildrenFunction = ({ + isOpen, + getItemProps, + toggleMenu, + }) => { + return ( +
+ {this.state.weeks} {this.state.semester} -
); } From 97161d034491337b5ac1f7dd6cbf325ddc260c06 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sun, 26 Jul 2020 15:26:42 +0800 Subject: [PATCH 27/48] Streamline definition of add custom module action --- website/src/actions/timetables.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index 8b2540ad06..80e19c3714 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -18,6 +18,7 @@ import { getModuleTimetable } from 'utils/modules'; // Actions that should not be used directly outside of thunks export const SET_TIMETABLE = 'SET_TIMETABLE' as const; export const ADD_MODULE = 'ADD_MODULE' as const; +export const ADD_CUSTOM_MODULE = 'ADD_CUSTOM_MODULE' as const; export const Internal = { setTimetable( semester: Semester, @@ -66,6 +67,19 @@ export function addModule(semester: Semester, moduleCode: ModuleCode) { }); } +export function addCustomModule(semester: Semester, moduleCode: ModuleCode, module: Module) { + const lessons = getModuleTimetable(module, semester); + const moduleLessonConfig = randomModuleLessonConfig(lessons); + return { + type: ADD_CUSTOM_MODULE, + payload: { + semester, + moduleCode, + moduleLessonConfig, + }, + }; +} + export const REMOVE_MODULE = 'REMOVE_MODULE' as const; export function removeModule(semester: Semester, moduleCode: ModuleCode) { return { From 30c03eb1f28531fea4c75176d45a010aae65b1aa Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sun, 26 Jul 2020 15:27:12 +0800 Subject: [PATCH 28/48] Set reducer for case ADD_CUSTOM_MODULE action --- website/src/reducers/timetables.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/src/reducers/timetables.ts b/website/src/reducers/timetables.ts index 2fc94d7da2..a5848e3f2a 100644 --- a/website/src/reducers/timetables.ts +++ b/website/src/reducers/timetables.ts @@ -10,6 +10,7 @@ import { ColorMapping, TimetablesState } from 'types/reducers'; import config from 'config'; import { ADD_MODULE, + ADD_CUSTOM_MODULE, CHANGE_LESSON, HIDE_LESSON_IN_TIMETABLE, REMOVE_MODULE, @@ -104,6 +105,7 @@ function semTimetable( switch (action.type) { case ADD_MODULE: + case ADD_CUSTOM_MODULE: return { ...state, [moduleCode]: action.payload.moduleLessonConfig, @@ -129,6 +131,7 @@ function semColors(state: ColorMapping = defaultSemColorMap, action: Actions): C switch (action.type) { case ADD_MODULE: + case ADD_CUSTOM_MODULE: return { ...state, [moduleCode]: getNewColor(values(state)), @@ -154,7 +157,6 @@ function semHiddenModules(state = defaultHiddenState, action: Actions) { if (!action.payload) { return state; } - switch (action.type) { case HIDE_LESSON_IN_TIMETABLE: return [action.payload.moduleCode, ...state]; @@ -194,6 +196,7 @@ function timetables( } case ADD_MODULE: + case ADD_CUSTOM_MODULE: case REMOVE_MODULE: case SELECT_MODULE_COLOR: case CHANGE_LESSON: From aceee7f8a2ff617ce2fe58e2d3ec2ea772c763e7 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Sun, 26 Jul 2020 15:30:36 +0800 Subject: [PATCH 29/48] Remove dummy data from form --- .../timetable/CustomModulesContainer.tsx | 9 ++- .../src/views/timetable/CustomModulesForm.tsx | 67 +++++++------------ .../src/views/timetable/TimetableContent.tsx | 1 - 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/website/src/views/timetable/CustomModulesContainer.tsx b/website/src/views/timetable/CustomModulesContainer.tsx index 5d0edc47a6..94c261c440 100644 --- a/website/src/views/timetable/CustomModulesContainer.tsx +++ b/website/src/views/timetable/CustomModulesContainer.tsx @@ -5,11 +5,18 @@ import { connect } from 'react-redux'; import CustomModulesForm from 'views/timetable/CustomModulesForm'; import styles from './CustomModulesContainer.scss'; +type OwnProps = { + // Own props + semester: number; +}; + +type Props = OwnProps; + type State = { showCustomModulesForm: boolean, }; -class CustomModulesAddContainer extends React.Component { +class CustomModulesAddContainer extends React.Component { state = { showCustomModulesForm: false, }; diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 1ec731727d..985bcd0c40 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import classnames from 'classnames'; -import { addModule } from 'actions/timetables'; +import { addModule, addCustomModule } from 'actions/timetables'; import { Module, AcadYear, @@ -18,12 +18,12 @@ import CustomModuleTimetableForm from './CustomModuleTimetableForm' import styles from './CustomModulesForm.scss'; type OwnProps = { - // Own props activeSemester: Semester; }; type Props = OwnProps & { addModule: (semester: Semester, moduleCode: ModuleCode) => void; + addCustomModule: (semester: Semester, moduleCode: ModuleCode, module: Module) => void; } type State = { @@ -45,25 +45,6 @@ type State = { timestamp: number; }; - -const semesterOneData = { - semester: 1, - timetable: [ - { - classNo: 'A1', - startTime: '1400', - endTime: '1700', - weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], - venue: 'UTSRC-LT51', - day: 'Thursday', - lessonType: 'Sectional Teaching', - size: 20, - }, - ], - examDate: '2018-12-06T13:00:00.000+08:00', - examDuration: 120, -}; - class CustomModulesForm extends React.Component { state: State = { acadYear: '', @@ -86,7 +67,25 @@ class CustomModulesForm extends React.Component { onSubmit = (evt: React.SyntheticEvent) => { evt.preventDefault(); - let customModule: Module = { + const semesterOneData = { + semester: this.state.semester, + timetable: [ + { + classNo: this.state.classNo, + startTime: this.state.startTime, + endTime: this.state.endTime, + weeks: this.state.weeks, + venue: this.state.venue, + day: this.state.day, + lessonType: this.state.lessonType, + size: 20, + }, + ], + examDate: '', + examDuration: 0, + }; + + const customModule: Module = { acadYear: this.state.acadYear, moduleCode: this.state.moduleCode, title: this.state.title, @@ -94,24 +93,9 @@ class CustomModulesForm extends React.Component { department: this.state.department, faculty: this.state.faculty, timestamp: Date.now() || this.state.timestamp, - semesterData: [], - }; - customModule = { - acadYear: '2018/2019', - description: 'This course aims to help students understand the role of information...', - preclusion: 'Students who have passed FNA1006', - faculty: 'Business', - department: 'Accounting', - title: 'Accounting Information Systems', - workload: [0, 3, 0, 4, 3], - prerequisite: 'FNA1002 or ACC1002', - moduleCredit: '4', - moduleCode: 'MA1101R', - semesterData: [semesterOneData], - timestamp: Date.now(), + semesterData: [semesterOneData,], }; - this.state.currentTimetableIndex += 1; - this.props.addModule(1, customModule.moduleCode); + this.props.addCustomModule(this.state.semester, customModule.moduleCode, customModule); }; onChangeClassNo = (event: React.ChangeEvent) => { @@ -155,6 +139,7 @@ class CustomModulesForm extends React.Component { this.state.classNo && this.state.startTime.length === 4 && this.state.endTime.length === 4 && + this.state.startTime < this.state.endTime && this.state.venue && this.state.day && this.state.lessonType, @@ -198,8 +183,6 @@ class CustomModulesForm extends React.Component { > Create Module - {this.state.weeks} - {this.state.semester}
); } @@ -210,4 +193,4 @@ const mapStateToProps = (state: StoreState) => { activeSemester: state.app.activeSemester, }; }; -export default connect(mapStateToProps, { addModule })(CustomModulesForm); +export default connect(mapStateToProps, { addModule, addCustomModule })(CustomModulesForm); diff --git a/website/src/views/timetable/TimetableContent.tsx b/website/src/views/timetable/TimetableContent.tsx index 9a536df814..8cf3a10c59 100644 --- a/website/src/views/timetable/TimetableContent.tsx +++ b/website/src/views/timetable/TimetableContent.tsx @@ -421,7 +421,6 @@ class TimetableContent extends React.Component { />
)} From e5bc99624e3de6b6bd2abab8f6690151f8b58f4c Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Tue, 28 Jul 2020 00:08:22 +0800 Subject: [PATCH 30/48] Add module key to add custom module action --- website/src/actions/timetables.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index 80e19c3714..07d2986f31 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -76,6 +76,7 @@ export function addCustomModule(semester: Semester, moduleCode: ModuleCode, modu semester, moduleCode, moduleLessonConfig, + module }, }; } From 5bc7ed12f957745be12fb96f44ac8575d6d1bc0e Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Tue, 28 Jul 2020 00:09:57 +0800 Subject: [PATCH 31/48] ModuleBank reducer handles add custom module action --- website/src/reducers/moduleBank.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/website/src/reducers/moduleBank.ts b/website/src/reducers/moduleBank.ts index bb0aece709..c335b5b8ef 100644 --- a/website/src/reducers/moduleBank.ts +++ b/website/src/reducers/moduleBank.ts @@ -9,6 +9,7 @@ import { UPDATE_MODULE_TIMESTAMP, SET_EXPORTED_DATA, } from 'actions/constants'; +import { ADD_CUSTOM_MODULE } from 'actions/timetables'; import { createMigrate, REHYDRATE } from 'redux-persist'; import { Module } from 'types/modules'; import { ModuleBank, ModuleList } from 'types/reducers'; @@ -52,6 +53,15 @@ function moduleBank(state: ModuleBank = defaultModuleBankState, action: Actions) [action.payload.moduleCode]: { ...action.payload, timestamp: Date.now() }, }, }; + + case ADD_CUSTOM_MODULE: + return { + ...state, + modules: { + ...state.modules, + [action.payload.moduleCode]: { ...action.payload.module, timestamp: Date.now() }, + }, + }; case UPDATE_MODULE_TIMESTAMP: return { From 4b872f9a1ec41040076b0c25605b28d9c7bb1325 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Tue, 28 Jul 2020 00:11:58 +0800 Subject: [PATCH 32/48] Set the default weeks to be 1-13 --- website/src/views/timetable/CustomModulesForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 985bcd0c40..e7966fae17 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -57,7 +57,7 @@ class CustomModulesForm extends React.Component { classNo: '', startTime: '', endTime: '', - weeks: [], + weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], venue: '', day: '', lessonType: '', From cca2bcad977d9adf825fb91c55f91ab822141cd1 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Tue, 28 Jul 2020 01:20:35 +0800 Subject: [PATCH 33/48] Add apostrophe to custom modules name, to handle conflict with current existing modules --- website/src/views/timetable/CustomModulesForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index e7966fae17..b52e4fac77 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -87,7 +87,7 @@ class CustomModulesForm extends React.Component { const customModule: Module = { acadYear: this.state.acadYear, - moduleCode: this.state.moduleCode, + moduleCode: `${this.state.moduleCode}'`, title: this.state.title, moduleCredit: this.state.moduleCredit, department: this.state.department, From 97475dc31cba9a92d75bd992b72f2d4c70d1a541 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Tue, 28 Jul 2020 01:21:30 +0800 Subject: [PATCH 34/48] In action of add custom module, check first whether there is an existing module code and validate the title --- website/src/actions/timetables.ts | 34 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index 07d2986f31..5e06d5e61b 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -68,16 +68,30 @@ export function addModule(semester: Semester, moduleCode: ModuleCode) { } export function addCustomModule(semester: Semester, moduleCode: ModuleCode, module: Module) { - const lessons = getModuleTimetable(module, semester); - const moduleLessonConfig = randomModuleLessonConfig(lessons); - return { - type: ADD_CUSTOM_MODULE, - payload: { - semester, - moduleCode, - moduleLessonConfig, - module - }, + return (dispatch: Function, getState: GetState) => { + const { modules } = getState().moduleBank; + if (Object.keys(modules).includes(moduleCode) && modules[moduleCode].title !== module.title) { + dispatch(openNotification(`Module ${moduleCode} exist with a different title`, { + action: { + text: '', + handler: () => dispatch(addCustomModule(semester, moduleCode, module)), + }, + })); + return; + } + + const lessons = getModuleTimetable(module, semester); + const moduleLessonConfig = randomModuleLessonConfig(lessons); + dispatch({ + type: ADD_CUSTOM_MODULE, + payload: { + semester, + moduleCode, + moduleLessonConfig, + module + }, + }) + }; } From e8ec45a44bdeb270ea1a47b379982e2dc94c3553 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Tue, 28 Jul 2020 02:17:55 +0800 Subject: [PATCH 35/48] Add check if the current module code has existed in store --- website/src/actions/timetables.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index 5e06d5e61b..b5ecb8b3c0 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -71,6 +71,7 @@ export function addCustomModule(semester: Semester, moduleCode: ModuleCode, modu return (dispatch: Function, getState: GetState) => { const { modules } = getState().moduleBank; if (Object.keys(modules).includes(moduleCode) && modules[moduleCode].title !== module.title) { + console.log("A") dispatch(openNotification(`Module ${moduleCode} exist with a different title`, { action: { text: '', @@ -80,6 +81,18 @@ export function addCustomModule(semester: Semester, moduleCode: ModuleCode, modu return; } + // const customModule = module; + const customModule = module; + console.log(customModule) + /* + if (Object.keys(modules).includes(moduleCode)) { + console.log("AAA") + const storedModule = modules[moduleCode]; + customModule.semesterData = [...storedModule.semesterData, customModule.semesterData[0]] + console.log(customModule); + } + */ + const lessons = getModuleTimetable(module, semester); const moduleLessonConfig = randomModuleLessonConfig(lessons); dispatch({ From 85c3047511fa15af17839f1bbc0a4e9aa097c225 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 00:06:17 +0800 Subject: [PATCH 36/48] Add check whether the custom module code and title is already present --- website/src/actions/timetables.ts | 75 +++++++++++-------- .../views/timetable/TimetableContainer.tsx | 1 - 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index b5ecb8b3c0..de89966c14 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -3,7 +3,7 @@ import { each, flatMap } from 'lodash'; import { Lesson, ColorIndex, ModuleLessonConfig, SemTimetableConfig } from 'types/timetables'; import { GetState } from 'types/redux'; import { ColorMapping } from 'types/reducers'; -import { ClassNo, LessonType, Module, ModuleCode, Semester } from 'types/modules'; +import { ClassNo, LessonType, Module, ModuleCode, Semester, SemesterData } from 'types/modules'; import { fetchModule } from 'actions/moduleBank'; import { openNotification } from 'actions/app'; @@ -14,6 +14,7 @@ import { validateTimetableModules, } from 'utils/timetables'; import { getModuleTimetable } from 'utils/modules'; +import { current } from 'immer'; // Actions that should not be used directly outside of thunks export const SET_TIMETABLE = 'SET_TIMETABLE' as const; @@ -41,6 +42,18 @@ export const Internal = { }, }; }, + + addCustomModule(semester: Semester, moduleCode: ModuleCode, moduleLessonConfig: ModuleLessonConfig, module: Module) { + return { + type: ADD_CUSTOM_MODULE, + payload: { + semester, + moduleCode, + moduleLessonConfig, + module + }, + } + } }; export function addModule(semester: Semester, moduleCode: ModuleCode) { @@ -70,41 +83,41 @@ export function addModule(semester: Semester, moduleCode: ModuleCode) { export function addCustomModule(semester: Semester, moduleCode: ModuleCode, module: Module) { return (dispatch: Function, getState: GetState) => { const { modules } = getState().moduleBank; - if (Object.keys(modules).includes(moduleCode) && modules[moduleCode].title !== module.title) { - console.log("A") - dispatch(openNotification(`Module ${moduleCode} exist with a different title`, { - action: { - text: '', - handler: () => dispatch(addCustomModule(semester, moduleCode, module)), - }, - })); - return; + if (Object.keys(modules).includes(moduleCode)) { + if (modules[moduleCode].title !== module.title) { + dispatch(openNotification(`Module ${moduleCode} exist with a different title`, { + action: { + text: '', + handler: () => dispatch(addCustomModule(semester, moduleCode, module)), + }, + })); + return; + } } + + let customModule = module; - // const customModule = module; - const customModule = module; - console.log(customModule) - /* if (Object.keys(modules).includes(moduleCode)) { - console.log("AAA") - const storedModule = modules[moduleCode]; - customModule.semesterData = [...storedModule.semesterData, customModule.semesterData[0]] - console.log(customModule); + console.log("A") + const storedModule = modules[moduleCode]; + const selectedSemester = getState().app.activeSemester; + const modifiedCurrentSemesterData = storedModule.semesterData.map(currentSemesterData => + currentSemesterData.semester === selectedSemester ? + { + ...currentSemesterData, + timetable: ([...currentSemesterData.timetable, customModule.semesterData[0].timetable[0]]) + } + : + currentSemesterData + ); + // console.log(modifiedCurrentSemesterData); + customModule = { ...storedModule, semesterData: modifiedCurrentSemesterData }; + // console.log(storedModule); } - */ - - const lessons = getModuleTimetable(module, semester); + const lessons = getModuleTimetable(customModule, semester); const moduleLessonConfig = randomModuleLessonConfig(lessons); - dispatch({ - type: ADD_CUSTOM_MODULE, - payload: { - semester, - moduleCode, - moduleLessonConfig, - module - }, - }) - + dispatch(Internal.addCustomModule(semester, moduleCode, moduleLessonConfig, customModule)); + // const customModule = module; }; } diff --git a/website/src/views/timetable/TimetableContainer.tsx b/website/src/views/timetable/TimetableContainer.tsx index cefb7eec5a..51fe2e9ed2 100644 --- a/website/src/views/timetable/TimetableContainer.tsx +++ b/website/src/views/timetable/TimetableContainer.tsx @@ -101,7 +101,6 @@ export class TimetableContainerComponent extends React.PureComponent moduleCodes.add(moduleCode)); } - // TODO: Account for loading error return Array.from(moduleCodes).some((moduleCode) => !modules[moduleCode]); } From 0c0177084037c19bde63e1455ecddeb31f88b6f2 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 02:56:39 +0800 Subject: [PATCH 37/48] Add functions to handle change on form and change form to select not downshift --- .../timetable/CustomModuleTimetableForm.scss | 13 + .../timetable/CustomModuleTimetableForm.tsx | 364 +++++++----------- .../src/views/timetable/CustomModulesForm.tsx | 8 +- 3 files changed, 152 insertions(+), 233 deletions(-) diff --git a/website/src/views/timetable/CustomModuleTimetableForm.scss b/website/src/views/timetable/CustomModuleTimetableForm.scss index c70b95a429..65348cf796 100644 --- a/website/src/views/timetable/CustomModuleTimetableForm.scss +++ b/website/src/views/timetable/CustomModuleTimetableForm.scss @@ -168,6 +168,19 @@ $btn-margin: 0.5rem; border: 0; } +.search { + label { + margin-bottom: 0; + font-weight: $font-weight-bold; + font-size: $font-size-s; + } + + :global(.form-group) { + margin-bottom: 0; + } +} + + /*.modalContent { display: flex; font-size: 1.4rem; diff --git a/website/src/views/timetable/CustomModuleTimetableForm.tsx b/website/src/views/timetable/CustomModuleTimetableForm.tsx index 52bbb976ac..2215e8a032 100644 --- a/website/src/views/timetable/CustomModuleTimetableForm.tsx +++ b/website/src/views/timetable/CustomModuleTimetableForm.tsx @@ -6,7 +6,6 @@ import { Days } from 'types/modules'; import { LESSON_TYPE_ABBREV } from '../../utils/timetables'; import ExportMenu from './ExportMenu'; - import styles from './CustomModuleTimetableForm.scss'; type OwnProps = { @@ -34,134 +33,108 @@ type State = { export default class CustomModuleTimetableForm extends React.Component { state: State = { selectedLessonType: '', - selectedDay: '', - selectedStartTimeHH: '', - selectedStartTimeMM: '', - selectedEndTimeHH: '', - selectedEndTimeMM: '', + selectedDay: 'Monday', + selectedStartTimeHH: '08', + selectedStartTimeMM: '00', + selectedEndTimeHH: '10', + selectedEndTimeMM: '00', + }; + + + /* + onUpdateInner = ( + event: React.SyntheticEvent, + key: keyof ModuleClass, + additionalKey?: string, + ) => { + if (typeof event.currentTarget.value !== 'undefined') { + let finalValue = event.currentTarget.value; + if (additionalKey) { + finalValue = (additionalKey == 'hour') ? + +event.currentTarget.value + this.props.moduleClass.startTime.slice(-2, 0) + : this.props.moduleClass.startTime.slice(0, 2) + +event.currentTarget.value; + } + this.props.onChange({ + ...this.props.moduleClass, + [key]: finalValue, + }); + } + }; + */ + + onSelectLessonType = (item: string) => { + this.props.onSelectLessonType(item); }; - renderDropdownLessonType: ChildrenFunction = ({ - isOpen, - getItemProps, - toggleMenu, - }) => { + renderDropdownLessonType = () => { return ( -
- -
+ +
); }; - onSelectLessonType = (selectedItem: string | null) => { - if (selectedItem === null) { - console.log('invalid'); - return; - } - const item: string = selectedItem || ''; - this.setState({ selectedLessonType: item }); - this.props.onSelectLessonType(item); - }; - onSelectDay = (selectedItem: string | null) => { - if (selectedItem === null) { - console.log('invalid'); - return; - } - const item: string = selectedItem || ''; - this.setState({ selectedDay: item }); + onSelectDay = (item: string) => { this.props.onSelectDay(item); - }; + } - renderDropdownDay: ChildrenFunction = ({ - isOpen, - getItemProps, - toggleMenu, - }) => { + renderDropdownDay = () => { return ( -
- -
+ +
+ + +
-
); }; - onSelectStartTimeHH = (selectedItem: string | null) => { - const item: string = selectedItem || ''; - this.setState({ selectedStartTimeHH: item }); - this.props.onSelectStartTime(item, 'HH'); + onSelectStartTimeHH = (selectedItem: string) => { + this.props.onSelectStartTime(selectedItem, 'HH'); }; - onSelectStartTimeMM = (selectedItem: string | null) => { - const item: string = selectedItem || '00'; - this.setState({ selectedStartTimeMM: item }); - this.props.onSelectStartTime(item, 'MM'); + onSelectStartTimeMM = (selectedItem: string) => { + this.props.onSelectStartTime(selectedItem, 'MM'); }; - renderDropdownStartTimeHH: ChildrenFunction = ({ - isOpen, - getItemProps, - toggleMenu, - }) => { + renderDropdownStartTimeHH: ChildrenFunction = () => { return (
-
); }; - renderDropdownStartTimeMM: ChildrenFunction = ({ - isOpen, - getItemProps, - toggleMenu, - }) => { + renderDropdownStartTimeMM: ChildrenFunction = () => { return ( -
-
); }; - renderDropdownEndTimeMM: ChildrenFunction = ({ - isOpen, - getItemProps, - toggleMenu, - }) => { + renderDropdownEndTimeMM: ChildrenFunction = () => { return ( -
- {this.state.showCustomModulesForm && }
From 0ca523af5de23565cbfe97095137fa41bbf7731a Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 19:47:34 +0800 Subject: [PATCH 39/48] Redefine all downshift to a select element, give label, and handler function --- .../views/timetable/CustomModulesForm.scss | 122 ++++++- .../src/views/timetable/CustomModulesForm.tsx | 301 ++++++++++++++---- 2 files changed, 356 insertions(+), 67 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index 13022e9b78..23c5b3a6e4 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -15,11 +15,7 @@ $btn-margin: 0.5rem; composes: form-control from global; text-align: left; white-space: nowrap; - display: inline-block; - - &:global(.form-control) { - height: $input-height; - } + width: 100%; &:not(:disabled) { box-shadow: inset 2px 2px 4px rgba(#000, 0.1); @@ -56,6 +52,81 @@ $btn-margin: 0.5rem; } } +.formGroup { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-left: -$btn-margin; // Negate childrens' left margins + + > * { + flex: 1 1 calc(33.333% - 1rem); + flex-grow: 1; + margin-bottom: $btn-margin; + margin-left: $btn-margin; + } + + @include media-breakpoint-down(sm) { + > * { + flex: 1 1 calc(50% - 1rem); + } + } + + @include media-breakpoint-down(xs) { + > * { + flex: 1 1 100%; + } + } + + @include media-breakpoint-up(md) { + :global(.btn) { + padding: 0.25rem 0.75rem; + //font-size: $font-size-sm; + } + } + + @include vertical-mode { + @include media-breakpoint-down(lg) { + > * { + flex: 1 1 calc(50% - 1rem); + } + } + } + +} + + +.weeksGroup { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-left: -$btn-margin; // Negate childrens' left margins + + > * { + flex: 1 1 calc(100%/13 - 55rem); + flex-grow: 1; + margin-bottom: $btn-margin; + margin-left: $btn-margin; + } + + @include media-breakpoint-down(sm) { + > * { + flex: 1 1 calc(100%/7 - 40rem); + } + } + + @include media-breakpoint-down(xs) { + > * { + flex: 1 1 calc(100%/7 - 40rem); + } + } + + @include media-breakpoint-up(md) { + :global(.btn) { + padding: 0.25rem 0.75rem; + //font-size: $font-size-sm; + } + } +} // Two layout forms: // @@ -77,4 +148,43 @@ $btn-margin: 0.5rem; @include media-breakpoint-down(sm) { @include fullWidthGroup; -} \ No newline at end of file +} + +.search { + label { + margin-bottom: 0; + font-weight: $font-weight-bold; + font-size: $font-size-s; + } + + :global(.form-group) { + margin-bottom: 0; + } +} + +$v-padding: 0.6rem; +$h-padding: $grid-gutter-width / 2; + +.label { + margin: 0; + font-size: $font-size-xs; + + &, + label, + input { + cursor: pointer; + } + + &.enabled { + font-weight: bold; + color: theme-color(); + } + + :global(.text-muted) { + font-weight: normal; + } + + @include touchscreen-only { + font-size: $font-size-sm; + } +} diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 2cae5077fb..bf5e255117 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import classnames from 'classnames'; import { addModule, addCustomModule } from 'actions/timetables'; import { + Days, Module, AcadYear, ModuleCode, @@ -14,6 +15,8 @@ import { Semester, } from 'types/modules'; import { State as StoreState } from 'types/state'; +import CheckboxItem from 'views/components/filters/CheckboxItem'; +import { LESSON_TYPE_ABBREV } from '../../utils/timetables'; import CustomModuleTimetableForm from './CustomModuleTimetableForm' import styles from './CustomModulesForm.scss'; @@ -26,6 +29,42 @@ type Props = OwnProps & { addCustomModule: (semester: Semester, moduleCode: ModuleCode, module: Module) => void; } +const hours = [ + '0800', + '0830', + '0900', + '0930', + '1000', + '1030', + '1100', + '1130', + '1200', + '1230', + '1300', + '1330', + '1400', + '1430', + '1500', + '1530', + '1600', + '1630', + '1700', + '1730', + '1800', + '1830', + '1900', + '1930', + '2000', + '2030', + '2100', + '2130', + '2200', + '2230', + '2300', + '2330', + '2400', +]; + export type ModuleClass = { acadYear: AcadYear; moduleCode: ModuleCode; @@ -41,7 +80,6 @@ export type ModuleClass = { venue: string; day: string; lessonType: string; - currentTimetableIndex: number; timestamp: number; }; @@ -52,18 +90,17 @@ class CustomModulesForm extends React.Component { acadYear: '', moduleCode: '', title: '', - moduleCredit: '', + moduleCredit: '1', department: '', faculty: '', semester: this.props.activeSemester, classNo: '', - startTime: '', - endTime: '', + startTime: '0800', + endTime: '1000', weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], venue: '', - day: '', + day: 'Monday', lessonType: '', - currentTimetableIndex: 0, timestamp: Date.now(), }; @@ -99,40 +136,191 @@ class CustomModulesForm extends React.Component { this.props.addCustomModule(this.state.semester, customModule.moduleCode, customModule); }; - onChangeClassNo = (event: React.ChangeEvent) => { - this.setState({ classNo: event.target.value }); + renderInputModuleCode = () => { + return ( +
+ + this.setState({ moduleCode: e.target.value })} + placeholder="Module Code" + /> +
+ ); }; - onSelectStartTime = (item: string, type: string) => { - this.setState((prevState) => ({ - startTime: - type === 'HH' - ? item + prevState.startTime.slice(-2, 0) - : prevState.startTime.slice(0, 2) + item, - })); + renderInputModuleTitle = () => { + return ( +
+ + this.setState({ title: e.target.value })} + placeholder="Module Title" + /> +
+ ); }; - onSelectEndTime = (item: string, type: string) => { - this.setState((prevState) => ({ - endTime: - type === 'HH' - ? item + prevState.endTime.slice(-2, 0) - : prevState.endTime.slice(0, 2) + item, - })); + renderInputModuleCredit = () => { + return ( +
+ + +
+ ); }; - onChangeVenue = (event: React.ChangeEvent) => { - this.setState({ venue: event.target.value }); + formatTime = (item: string) => { + if (item === '2400') { + return '00:00'; + } + return `${item.slice(0, 2)}:${item.slice(2, 4)}`; }; - onSelectDay = (item: string) => { - this.setState({ day: item }); + renderDropdownStartTime = () => { + return ( +
+ + +
+ ); }; - onSelectLessonType = (item: string) => { - this.setState({ lessonType: item }); + renderDropdownEndTime = () => { + return ( +
+ + +
+ ); + }; + + renderDropdownLessonType = () => { + return ( +
+ + +
+ ); }; + renderInputClassNo = () => { + return ( +
+ + this.setState({ classNo: e.target.value })} + placeholder="Class No" + /> +
+ ); + }; + + renderInputVenue = () => { + return ( +
+ + this.setState({ venue: e.target.value })} + placeholder="Venue" + /> +
+ ); + }; + + renderDropdownDay = () => { + return ( +
+ + +
+ ); + }; + + + + /* + renderChecklistWeeks = () => ( + Array.from(Array(13), (_, i) => i + 1).map((item) => ( + this.onSelectWeek(item)} + active + itemKey={`${item}`} + label={`${item}`} + count={0} + showCount={false} + disabled={false} + /> + )) + ) + */ + + + isFormValid = () => { const validity = Boolean( this.state.moduleCode && @@ -151,40 +339,31 @@ class CustomModulesForm extends React.Component { render() { return (
-
- this.setState({ moduleCode: e.target.value })} - placeholder="Module Code" - /> - this.setState({ title: e.target.value })} - placeholder="Module Title" - /> +
+
+ {this.renderInputModuleCode()} + {this.renderInputModuleTitle()} + {this.renderInputModuleCredit()} + {this.renderDropdownLessonType()} + {this.renderInputClassNo()} + {this.renderInputVenue()} + {this.renderDropdownDay()} + {this.renderDropdownStartTime()} + {this.renderDropdownEndTime()} +
+
- - -
); } From 7ca7bc9ff396024f448aadc970905c67afef937b Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 20:04:06 +0800 Subject: [PATCH 40/48] Change to form and use HTML validation --- .../src/views/timetable/CustomModulesForm.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index bf5e255117..96cdc8f09f 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -104,7 +104,7 @@ class CustomModulesForm extends React.Component { timestamp: Date.now(), }; - onSubmit = (evt: React.SyntheticEvent) => { + onSubmit = (evt: React.FormEvent) => { evt.preventDefault(); const semesterOneData = { semester: this.state.semester, @@ -145,6 +145,7 @@ class CustomModulesForm extends React.Component { className={classnames(styles.input, styles.titleIcon)} onChange={(e) => this.setState({ moduleCode: e.target.value })} placeholder="Module Code" + required />
); @@ -159,6 +160,7 @@ class CustomModulesForm extends React.Component { className={classnames(styles.input, styles.titleIcon)} onChange={(e) => this.setState({ title: e.target.value })} placeholder="Module Title" + required />
); @@ -262,6 +264,7 @@ class CustomModulesForm extends React.Component { className={classnames(styles.input, styles.titleIcon)} onChange={(e) => this.setState({ classNo: e.target.value })} placeholder="Class No" + required />
); @@ -276,6 +279,7 @@ class CustomModulesForm extends React.Component { className={classnames(styles.input, styles.titleIcon)} onChange={(e) => this.setState({ venue: e.target.value })} placeholder="Venue" + required />
); @@ -338,7 +342,7 @@ class CustomModulesForm extends React.Component { render() { return ( -
+
{ {this.renderDropdownStartTime()} {this.renderDropdownEndTime()}
- + />
-
+ ); } } From 014833291f4157805ba6332d78274bafb4b77ea5 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 20:17:48 +0800 Subject: [PATCH 41/48] Make flex grow to be 0 --- .../views/timetable/CustomModulesForm.scss | 35 +++---------- .../src/views/timetable/CustomModulesForm.tsx | 50 +++---------------- 2 files changed, 13 insertions(+), 72 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index 23c5b3a6e4..9782c56ae7 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -26,32 +26,11 @@ $btn-margin: 0.5rem; } } -.buttonGroup { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin-left: -$btn-margin; // Negate childrens' left margins - - > * { - flex-grow: 1; - margin-bottom: $btn-margin; - margin-left: $btn-margin; - } - - @include media-breakpoint-down(xs) { - > * { - flex: 1 1 100%; - } - } - - @include media-breakpoint-up(sm) { - :global(.btn) { - padding: 0.25rem 0.75rem; - font-size: $font-size-sm; - } - } +.submitBtn { + margin-top: 1rem; } + .formGroup { display: flex; flex-wrap: wrap; @@ -59,7 +38,7 @@ $btn-margin: 0.5rem; margin-left: -$btn-margin; // Negate childrens' left margins > * { - flex: 1 1 calc(33.333% - 1rem); + flex: 0 1 calc(33.333% - 1rem); flex-grow: 1; margin-bottom: $btn-margin; margin-left: $btn-margin; @@ -67,13 +46,13 @@ $btn-margin: 0.5rem; @include media-breakpoint-down(sm) { > * { - flex: 1 1 calc(50% - 1rem); + flex: 0 1 calc(50% - 1rem); } } @include media-breakpoint-down(xs) { > * { - flex: 1 1 100%; + flex: 0 1 100%; } } @@ -87,7 +66,7 @@ $btn-margin: 0.5rem; @include vertical-mode { @include media-breakpoint-down(lg) { > * { - flex: 1 1 calc(50% - 1rem); + flex: 0 1 calc(50% - 1rem); } } } diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 96cdc8f09f..a60f2bfd3a 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -15,9 +15,7 @@ import { Semester, } from 'types/modules'; import { State as StoreState } from 'types/state'; -import CheckboxItem from 'views/components/filters/CheckboxItem'; import { LESSON_TYPE_ABBREV } from '../../utils/timetables'; -import CustomModuleTimetableForm from './CustomModuleTimetableForm' import styles from './CustomModulesForm.scss'; type OwnProps = { @@ -85,12 +83,12 @@ export type ModuleClass = { type State = ModuleClass; -class CustomModulesForm extends React.Component { +class CustomModulesForm extends React.PureComponent { state: State = { acadYear: '', moduleCode: '', title: '', - moduleCredit: '1', + moduleCredit: '4', department: '', faculty: '', semester: this.props.activeSemester, @@ -100,7 +98,7 @@ class CustomModulesForm extends React.Component { weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], venue: '', day: 'Monday', - lessonType: '', + lessonType: 'Lecture', timestamp: Date.now(), }; @@ -119,8 +117,6 @@ class CustomModulesForm extends React.Component { lessonType: this.state.lessonType, }, ], - examDate: '', - examDuration: 0, }; const customModule: Module = { @@ -220,7 +216,7 @@ class CustomModulesForm extends React.Component {
From 439f6b19fa54f34d286e485c7298fcac4a51e455 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 20:18:08 +0800 Subject: [PATCH 42/48] Delete CustomMOduleTimetableForm component --- .../timetable/CustomModuleTimetableForm.scss | 205 ------------- .../timetable/CustomModuleTimetableForm.tsx | 284 ------------------ 2 files changed, 489 deletions(-) delete mode 100644 website/src/views/timetable/CustomModuleTimetableForm.scss delete mode 100644 website/src/views/timetable/CustomModuleTimetableForm.tsx diff --git a/website/src/views/timetable/CustomModuleTimetableForm.scss b/website/src/views/timetable/CustomModuleTimetableForm.scss deleted file mode 100644 index 65348cf796..0000000000 --- a/website/src/views/timetable/CustomModuleTimetableForm.scss +++ /dev/null @@ -1,205 +0,0 @@ -@import "./_variables.scss"; -@import '~styles/utils/modules-entry'; - -$input-height: 2.75rem; -$module-list-height: 13.5rem; -$item-padding-vertical: 0.6rem; -$btn-margin: 0.5rem; - -.formGroup { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin-left: -$btn-margin; // Negate childrens' left margins - - > * { - flex: 1 1 calc(33.333% - 1rem); - flex-grow: 1; - margin-bottom: $btn-margin; - margin-left: $btn-margin; - } - - @include media-breakpoint-down(sm) { - > * { - flex: 1 1 calc(50% - 1rem); - } - } - - @include media-breakpoint-down(xs) { - > * { - flex: 1 1 100%; - } - } - - @include media-breakpoint-up(md) { - :global(.btn) { - padding: 0.25rem 0.75rem; - //font-size: $font-size-sm; - } - } -} - - -// Two layout forms: -// -// 1. Left and right on one row, with space between two groups -// - md and above for horizontal -// -// 2. Left and right on 2 rows, full width (fullWidthGroup) -// - any size in vertical -// - sm and below for horizontal -@mixin fullWidthGroup { - .formGroup { - width: calc(100% + #{$btn-margin}); // Make full width and negate the left shifting caused by our margin-left - } -} - -@include media-breakpoint-down(sm) { - @include fullWidthGroup; -} - -.input { - composes: form-control from global; - text-align: left; - white-space: nowrap; - - &:global(.form-control) { - height: $input-height; - } - - &:not(:disabled) { - box-shadow: inset 2px 2px 4px rgba(#000, 0.1); - - @include night-mode { - box-shadow: inset 2px 2px 4px rgba(#000, 0.9); - } - } -} - -.dayMenu { - position: relative; - - .toggle { - width: 100%; - height: $input-height; - } - - .chevron { - margin: 0 -0.6rem 0 0.2rem; - } - - .dropdownMenu { - width: 100%; - - svg { - margin-right: 0.2rem; - vertical-align: middle; - } - - @include media-breakpoint-down(xs) { - width: auto; - } - } -} - -.timeMenu { - position: relative; - - .toggle { - width: 100%; - height: $input-height; - } - - .chevron { - margin: 0 -0.6rem 0 0.2rem; - } - - .dropdownMenu { - width: 100%; - - svg { - margin-right: 0.2rem; - vertical-align: middle; - } - - @include media-breakpoint-down(xs) { - width: auto; - } - } -} - -.time3Menu { - position: relative; - - .toggle { - width: 100%; - height: $input-height; - } - - .chevron { - margin: 0 -0.6rem 0 0.2rem; - } - - .dropdownMenu { - width: 3rem; - - svg { - margin-right: 0.2rem; - vertical-align: middle; - } - - @include media-breakpoint-down(xs) { - width: auto; - } - } -} - - - - -.timeInputHolder { - background-color: white !important; - color: red !important; - width: fit-content; -} - -.timeInput { - background-color: white; - border: 0; -} - -.search { - label { - margin-bottom: 0; - font-weight: $font-weight-bold; - font-size: $font-size-s; - } - - :global(.form-group) { - margin-bottom: 0; - } -} - - -/*.modalContent { - display: flex; - font-size: 1.4rem; - line-height: 1.3; - - svg { - $size: 3.8rem; - - width: $size; - height: $size; - margin-right: 1.2rem; - color: theme-color(warning); - } -} - -.modalButtons { - text-align: right; - - :global(.btn) { - margin-right: 0.5rem; - } -}*/ diff --git a/website/src/views/timetable/CustomModuleTimetableForm.tsx b/website/src/views/timetable/CustomModuleTimetableForm.tsx deleted file mode 100644 index 2215e8a032..0000000000 --- a/website/src/views/timetable/CustomModuleTimetableForm.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import * as React from 'react'; -import classnames from 'classnames'; -import { connect } from 'react-redux'; -import Downshift, { ChildrenFunction } from 'downshift'; -import { Days } from 'types/modules'; -import { LESSON_TYPE_ABBREV } from '../../utils/timetables'; -import ExportMenu from './ExportMenu'; - -import styles from './CustomModuleTimetableForm.scss'; - -type OwnProps = { - // Own props - index: number; - onChangeClassNo: (event: React.ChangeEvent) => void; - onSelectStartTime: (item: string, type: string) => void; - onSelectEndTime: (item: string, type: string) => void; - onChangeVenue: (event: React.ChangeEvent) => void; - onSelectDay: (item: string) => void; - onSelectLessonType: (item: string) => void; -}; - -type Props = OwnProps; - -type State = { - selectedLessonType: string, - selectedDay: string, - selectedStartTimeHH: string, - selectedStartTimeMM: string, - selectedEndTimeHH: string, - selectedEndTimeMM: string, -} - -export default class CustomModuleTimetableForm extends React.Component { - state: State = { - selectedLessonType: '', - selectedDay: 'Monday', - selectedStartTimeHH: '08', - selectedStartTimeMM: '00', - selectedEndTimeHH: '10', - selectedEndTimeMM: '00', - }; - - - /* - onUpdateInner = ( - event: React.SyntheticEvent, - key: keyof ModuleClass, - additionalKey?: string, - ) => { - if (typeof event.currentTarget.value !== 'undefined') { - let finalValue = event.currentTarget.value; - if (additionalKey) { - finalValue = (additionalKey == 'hour') ? - +event.currentTarget.value + this.props.moduleClass.startTime.slice(-2, 0) - : this.props.moduleClass.startTime.slice(0, 2) + +event.currentTarget.value; - } - this.props.onChange({ - ...this.props.moduleClass, - [key]: finalValue, - }); - } - }; - */ - - onSelectLessonType = (item: string) => { - this.props.onSelectLessonType(item); - }; - - renderDropdownLessonType = () => { - return ( -
- - -
- ); - }; - - - onSelectDay = (item: string) => { - this.props.onSelectDay(item); - } - - renderDropdownDay = () => { - return ( -
- -
- - -
-
- ); - }; - - onSelectStartTimeHH = (selectedItem: string) => { - this.props.onSelectStartTime(selectedItem, 'HH'); - }; - - onSelectStartTimeMM = (selectedItem: string) => { - this.props.onSelectStartTime(selectedItem, 'MM'); - }; - - renderDropdownStartTimeHH: ChildrenFunction = () => { - return ( -
-
- - -
-
- ); - }; - - renderDropdownStartTimeMM: ChildrenFunction = () => { - return ( -
-
- - -
-
- ); - }; - - renderDropdownEndTimeHH: ChildrenFunction = () => { - return ( -
-
- - -
-
- ); - }; - - renderDropdownEndTimeMM: ChildrenFunction = () => { - return ( -
-
- - -
-
- ); - }; - - - - onSelectEndTimeHH = (selectedItem: string) => { - this.props.onSelectEndTime(selectedItem, 'HH'); - }; - - onSelectEndTimeMM = (selectedItem: string) => { - this.props.onSelectEndTime(selectedItem, 'MM'); - }; - - render() { - return ( -
-
- this.props.onChangeClassNo(e)} - placeholder="Class No." - /> - {this.renderDropdownLessonType} - this.props.onChangeVenue(e)} - placeholder="Venue." - /> - {this.renderDropdownDay()} - -
- {this.renderDropdownStartTimeHH}:{this.renderDropdownStartTimeMM} -
- -
- {this.renderDropdownEndTimeHH}:{this.renderDropdownEndTimeMM} -
-
-
- ); - } -} \ No newline at end of file From aa281e5a59a94070d0630e12965b1fb619f6dd9a Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 20:48:52 +0800 Subject: [PATCH 43/48] Lint fix --- website/src/actions/timetables.ts | 58 ++++++++++--------- website/src/reducers/moduleBank.ts | 2 +- website/src/types/modules.ts | 2 +- .../timetable/CustomModulesContainer.scss | 4 +- .../timetable/CustomModulesContainer.tsx | 16 ++--- .../views/timetable/CustomModulesForm.scss | 8 +-- .../src/views/timetable/CustomModulesForm.tsx | 4 +- .../src/views/timetable/TimetableContent.tsx | 4 +- 8 files changed, 46 insertions(+), 52 deletions(-) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index de89966c14..61e888d3d5 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -3,7 +3,7 @@ import { each, flatMap } from 'lodash'; import { Lesson, ColorIndex, ModuleLessonConfig, SemTimetableConfig } from 'types/timetables'; import { GetState } from 'types/redux'; import { ColorMapping } from 'types/reducers'; -import { ClassNo, LessonType, Module, ModuleCode, Semester, SemesterData } from 'types/modules'; +import { ClassNo, LessonType, Module, ModuleCode, Semester } from 'types/modules'; import { fetchModule } from 'actions/moduleBank'; import { openNotification } from 'actions/app'; @@ -14,7 +14,6 @@ import { validateTimetableModules, } from 'utils/timetables'; import { getModuleTimetable } from 'utils/modules'; -import { current } from 'immer'; // Actions that should not be used directly outside of thunks export const SET_TIMETABLE = 'SET_TIMETABLE' as const; @@ -43,17 +42,22 @@ export const Internal = { }; }, - addCustomModule(semester: Semester, moduleCode: ModuleCode, moduleLessonConfig: ModuleLessonConfig, module: Module) { + addCustomModule( + semester: Semester, + moduleCode: ModuleCode, + moduleLessonConfig: ModuleLessonConfig, + module: Module, + ) { return { type: ADD_CUSTOM_MODULE, payload: { semester, moduleCode, moduleLessonConfig, - module + module, }, - } - } + }; + }, }; export function addModule(semester: Semester, moduleCode: ModuleCode) { @@ -85,39 +89,39 @@ export function addCustomModule(semester: Semester, moduleCode: ModuleCode, modu const { modules } = getState().moduleBank; if (Object.keys(modules).includes(moduleCode)) { if (modules[moduleCode].title !== module.title) { - dispatch(openNotification(`Module ${moduleCode} exist with a different title`, { - action: { - text: '', - handler: () => dispatch(addCustomModule(semester, moduleCode, module)), - }, - })); + dispatch( + openNotification(`Module ${moduleCode} exist with a different title`, { + action: { + text: '', + handler: () => dispatch(addCustomModule(semester, moduleCode, module)), + }, + }), + ); return; } } - + let customModule = module; if (Object.keys(modules).includes(moduleCode)) { - console.log("A") - const storedModule = modules[moduleCode]; - const selectedSemester = getState().app.activeSemester; - const modifiedCurrentSemesterData = storedModule.semesterData.map(currentSemesterData => - currentSemesterData.semester === selectedSemester ? - { + const storedModule = modules[moduleCode]; + const selectedSemester = getState().app.activeSemester; + const modifiedCurrentSemesterData = storedModule.semesterData.map((currentSemesterData) => + currentSemesterData.semester === selectedSemester + ? { ...currentSemesterData, - timetable: ([...currentSemesterData.timetable, customModule.semesterData[0].timetable[0]]) + timetable: [ + ...currentSemesterData.timetable, + customModule.semesterData[0].timetable[0], + ], } - : - currentSemesterData - ); - // console.log(modifiedCurrentSemesterData); - customModule = { ...storedModule, semesterData: modifiedCurrentSemesterData }; - // console.log(storedModule); + : currentSemesterData, + ); + customModule = { ...storedModule, semesterData: modifiedCurrentSemesterData }; } const lessons = getModuleTimetable(customModule, semester); const moduleLessonConfig = randomModuleLessonConfig(lessons); dispatch(Internal.addCustomModule(semester, moduleCode, moduleLessonConfig, customModule)); - // const customModule = module; }; } diff --git a/website/src/reducers/moduleBank.ts b/website/src/reducers/moduleBank.ts index c335b5b8ef..10ee821db5 100644 --- a/website/src/reducers/moduleBank.ts +++ b/website/src/reducers/moduleBank.ts @@ -53,7 +53,7 @@ function moduleBank(state: ModuleBank = defaultModuleBankState, action: Actions) [action.payload.moduleCode]: { ...action.payload, timestamp: Date.now() }, }, }; - + case ADD_CUSTOM_MODULE: return { ...state, diff --git a/website/src/types/modules.ts b/website/src/types/modules.ts index f0f90bf040..05c3949316 100644 --- a/website/src/types/modules.ts +++ b/website/src/types/modules.ts @@ -46,7 +46,7 @@ export const Days: readonly Day[] = [ 'Friday', 'Saturday', 'Sunday', -] +]; export const WorkingDays: readonly Day[] = [ 'Monday', diff --git a/website/src/views/timetable/CustomModulesContainer.scss b/website/src/views/timetable/CustomModulesContainer.scss index 5b600691ca..0006a9fd01 100644 --- a/website/src/views/timetable/CustomModulesContainer.scss +++ b/website/src/views/timetable/CustomModulesContainer.scss @@ -1,8 +1,6 @@ @import '~styles/utils/modules-entry'; .titleBtn { - - margin-bottom: 0.5rem; margin-top: 0.5rem; - + margin-bottom: 0.5rem; } \ No newline at end of file diff --git a/website/src/views/timetable/CustomModulesContainer.tsx b/website/src/views/timetable/CustomModulesContainer.tsx index 42fb16133c..121e2ade20 100644 --- a/website/src/views/timetable/CustomModulesContainer.tsx +++ b/website/src/views/timetable/CustomModulesContainer.tsx @@ -13,7 +13,7 @@ type OwnProps = { type Props = OwnProps; type State = { - showCustomModulesForm: boolean, + showCustomModulesForm: boolean; }; class CustomModulesAddContainer extends React.Component { @@ -23,10 +23,9 @@ class CustomModulesAddContainer extends React.Component { onChange = () => { if (this.state.showCustomModulesForm === true) { - this.setState({showCustomModulesForm: false }) - } - else { - this.setState({showCustomModulesForm: true }) + this.setState({ showCustomModulesForm: false }); + } else { + this.setState({ showCustomModulesForm: true }); } }; @@ -36,7 +35,7 @@ class CustomModulesAddContainer extends React.Component { @@ -47,10 +46,7 @@ class CustomModulesAddContainer extends React.Component { } function mapStateToProps() { - - return { - - }; + return {}; } export default connect(mapStateToProps, {})(CustomModulesAddContainer); diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index 9782c56ae7..f727e3ca0c 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -13,9 +13,9 @@ $btn-margin: 0.5rem; .input { composes: form-control from global; + width: 100%; text-align: left; white-space: nowrap; - width: 100%; &:not(:disabled) { box-shadow: inset 2px 2px 4px rgba(#000, 0.1); @@ -81,7 +81,7 @@ $btn-margin: 0.5rem; margin-left: -$btn-margin; // Negate childrens' left margins > * { - flex: 1 1 calc(100%/13 - 55rem); + flex: 1 1 calc(100% / 13 - 55rem); flex-grow: 1; margin-bottom: $btn-margin; margin-left: $btn-margin; @@ -89,13 +89,13 @@ $btn-margin: 0.5rem; @include media-breakpoint-down(sm) { > * { - flex: 1 1 calc(100%/7 - 40rem); + flex: 1 1 calc(100% / 7 - 40rem); } } @include media-breakpoint-down(xs) { > * { - flex: 1 1 calc(100%/7 - 40rem); + flex: 1 1 calc(100% / 7 - 40rem); } } diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index a60f2bfd3a..12e56c21d7 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -11,7 +11,6 @@ import { Department, Faculty, Weeks, - SemesterData, Semester, } from 'types/modules'; import { State as StoreState } from 'types/state'; @@ -25,7 +24,7 @@ type OwnProps = { type Props = OwnProps & { addModule: (semester: Semester, moduleCode: ModuleCode) => void; addCustomModule: (semester: Semester, moduleCode: ModuleCode, module: Module) => void; -} +}; const hours = [ '0800', @@ -301,7 +300,6 @@ class CustomModulesForm extends React.PureComponent { ); }; - render() { return (
diff --git a/website/src/views/timetable/TimetableContent.tsx b/website/src/views/timetable/TimetableContent.tsx index 8cf3a10c59..d42027a905 100644 --- a/website/src/views/timetable/TimetableContent.tsx +++ b/website/src/views/timetable/TimetableContent.tsx @@ -419,9 +419,7 @@ class TimetableContent extends React.Component { addModule={this.addModule} removeModule={this.removeModule} /> - +
)}
From 32a3fd2954c077338e4e1d2bb3a1d9721518ce38 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 21:04:55 +0800 Subject: [PATCH 44/48] Remove unneccessary styling --- .../views/timetable/CustomModulesForm.scss | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.scss b/website/src/views/timetable/CustomModulesForm.scss index f727e3ca0c..5be256d7eb 100644 --- a/website/src/views/timetable/CustomModulesForm.scss +++ b/website/src/views/timetable/CustomModulesForm.scss @@ -30,7 +30,6 @@ $btn-margin: 0.5rem; margin-top: 1rem; } - .formGroup { display: flex; flex-wrap: wrap; @@ -73,40 +72,6 @@ $btn-margin: 0.5rem; } - -.weeksGroup { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin-left: -$btn-margin; // Negate childrens' left margins - - > * { - flex: 1 1 calc(100% / 13 - 55rem); - flex-grow: 1; - margin-bottom: $btn-margin; - margin-left: $btn-margin; - } - - @include media-breakpoint-down(sm) { - > * { - flex: 1 1 calc(100% / 7 - 40rem); - } - } - - @include media-breakpoint-down(xs) { - > * { - flex: 1 1 calc(100% / 7 - 40rem); - } - } - - @include media-breakpoint-up(md) { - :global(.btn) { - padding: 0.25rem 0.75rem; - //font-size: $font-size-sm; - } - } -} - // Two layout forms: // // 1. Left and right on one row, with space between two groups @@ -141,9 +106,6 @@ $btn-margin: 0.5rem; } } -$v-padding: 0.6rem; -$h-padding: $grid-gutter-width / 2; - .label { margin: 0; font-size: $font-size-xs; From a1194e509c649b46bea09c265395ac0b27956a00 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Wed, 29 Jul 2020 21:18:20 +0800 Subject: [PATCH 45/48] Notification when clash module code to remove the last character --- website/src/actions/timetables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/actions/timetables.ts b/website/src/actions/timetables.ts index 61e888d3d5..541b422721 100644 --- a/website/src/actions/timetables.ts +++ b/website/src/actions/timetables.ts @@ -90,7 +90,7 @@ export function addCustomModule(semester: Semester, moduleCode: ModuleCode, modu if (Object.keys(modules).includes(moduleCode)) { if (modules[moduleCode].title !== module.title) { dispatch( - openNotification(`Module ${moduleCode} exist with a different title`, { + openNotification(`Module ${moduleCode.slice(0, -1)} exist with a different title`, { action: { text: '', handler: () => dispatch(addCustomModule(semester, moduleCode, module)), From 4c8df8ec0893a0ef694bb97f8697b2f2eff56373 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Mon, 10 Aug 2020 02:26:26 +0800 Subject: [PATCH 46/48] Change input to button for submit, rename variable according to review --- .../src/views/timetable/CustomModulesForm.tsx | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 12e56c21d7..c15af83d62 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -19,6 +19,7 @@ import styles from './CustomModulesForm.scss'; type OwnProps = { activeSemester: Semester; + stateLOL: State; }; type Props = OwnProps & { @@ -103,7 +104,7 @@ class CustomModulesForm extends React.PureComponent { onSubmit = (evt: React.FormEvent) => { evt.preventDefault(); - const semesterOneData = { + const semesterDatum = { semester: this.state.semester, timetable: [ { @@ -126,9 +127,11 @@ class CustomModulesForm extends React.PureComponent { department: this.state.department, faculty: this.state.faculty, timestamp: Date.now() || this.state.timestamp, - semesterData: [semesterOneData], + semesterData: [semesterDatum], }; + console.log(this.props.stateLOL) this.props.addCustomModule(this.state.semester, customModule.moduleCode, customModule); + }; renderInputModuleCode = () => { @@ -165,18 +168,15 @@ class CustomModulesForm extends React.PureComponent { return (
- + type="number" + step="1" + min="0" + />
); }; @@ -319,11 +319,11 @@ class CustomModulesForm extends React.PureComponent { {this.renderDropdownStartTime()} {this.renderDropdownEndTime()} - + >Create Module + ); @@ -333,6 +333,7 @@ class CustomModulesForm extends React.PureComponent { const mapStateToProps = (state: StoreState) => { return { activeSemester: state.app.activeSemester, + stateLOL: state }; }; export default connect(mapStateToProps, { addModule, addCustomModule })(CustomModulesForm); From 55c1b13152e1fed30d9ac838a25cc43b50db7a8f Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Mon, 10 Aug 2020 02:36:30 +0800 Subject: [PATCH 47/48] Remove unchanged state from state and put it when submitting form --- .../src/views/timetable/CustomModulesForm.tsx | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index c15af83d62..4f19e5ab3f 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -19,7 +19,6 @@ import styles from './CustomModulesForm.scss'; type OwnProps = { activeSemester: Semester; - stateLOL: State; }; type Props = OwnProps & { @@ -64,54 +63,43 @@ const hours = [ ]; export type ModuleClass = { - acadYear: AcadYear; moduleCode: ModuleCode; title: ModuleTitle; - department: Department; - faculty: Faculty; moduleCredit: string; - semester: Semester; classNo: string; startTime: string; endTime: string; - weeks: Weeks; venue: string; day: string; lessonType: string; - timestamp: number; }; type State = ModuleClass; class CustomModulesForm extends React.PureComponent { state: State = { - acadYear: '', moduleCode: '', title: '', moduleCredit: '4', - department: '', - faculty: '', - semester: this.props.activeSemester, classNo: '', startTime: '0800', endTime: '1000', - weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], venue: '', day: 'Monday', lessonType: 'Lecture', - timestamp: Date.now(), }; onSubmit = (evt: React.FormEvent) => { evt.preventDefault(); + const semesterDatum = { - semester: this.state.semester, + semester: this.props.activeSemester, timetable: [ { classNo: this.state.classNo, startTime: this.state.startTime, endTime: this.state.endTime, - weeks: this.state.weeks, + weeks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], venue: this.state.venue, day: this.state.day, lessonType: this.state.lessonType, @@ -120,17 +108,16 @@ class CustomModulesForm extends React.PureComponent { }; const customModule: Module = { - acadYear: this.state.acadYear, + acadYear: '', moduleCode: `${this.state.moduleCode}'`, title: this.state.title, moduleCredit: this.state.moduleCredit, - department: this.state.department, - faculty: this.state.faculty, - timestamp: Date.now() || this.state.timestamp, + department: '', + faculty: '', + timestamp: Date.now(), semesterData: [semesterDatum], }; - console.log(this.props.stateLOL) - this.props.addCustomModule(this.state.semester, customModule.moduleCode, customModule); + this.props.addCustomModule(semesterDatum.semester, customModule.moduleCode, customModule); }; @@ -333,7 +320,6 @@ class CustomModulesForm extends React.PureComponent { const mapStateToProps = (state: StoreState) => { return { activeSemester: state.app.activeSemester, - stateLOL: state }; }; export default connect(mapStateToProps, { addModule, addCustomModule })(CustomModulesForm); From a32aace76a85891af73b60879c628376b2e79603 Mon Sep 17 00:00:00 2001 From: agnesnatasya Date: Mon, 10 Aug 2020 02:44:44 +0800 Subject: [PATCH 48/48] Lint fix and add '-custom-module' suffix to the id to ensure it is unique --- .../src/views/timetable/CustomModulesForm.tsx | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/website/src/views/timetable/CustomModulesForm.tsx b/website/src/views/timetable/CustomModulesForm.tsx index 4f19e5ab3f..4663a81071 100644 --- a/website/src/views/timetable/CustomModulesForm.tsx +++ b/website/src/views/timetable/CustomModulesForm.tsx @@ -2,17 +2,7 @@ import * as React from 'react'; import { connect } from 'react-redux'; import classnames from 'classnames'; import { addModule, addCustomModule } from 'actions/timetables'; -import { - Days, - Module, - AcadYear, - ModuleCode, - ModuleTitle, - Department, - Faculty, - Weeks, - Semester, -} from 'types/modules'; +import { Days, Module, ModuleCode, ModuleTitle, Semester } from 'types/modules'; import { State as StoreState } from 'types/state'; import { LESSON_TYPE_ABBREV } from '../../utils/timetables'; import styles from './CustomModulesForm.scss'; @@ -118,15 +108,14 @@ class CustomModulesForm extends React.PureComponent { semesterData: [semesterDatum], }; this.props.addCustomModule(semesterDatum.semester, customModule.moduleCode, customModule); - }; renderInputModuleCode = () => { return (
- + this.setState({ moduleCode: e.target.value })} placeholder="Module Code" @@ -139,9 +128,9 @@ class CustomModulesForm extends React.PureComponent { renderInputModuleTitle = () => { return (
- + this.setState({ title: e.target.value })} placeholder="Module Title" @@ -154,9 +143,9 @@ class CustomModulesForm extends React.PureComponent { renderInputModuleCredit = () => { return (
- + this.setState({ moduleCredit: e.target.value })} @@ -178,9 +167,9 @@ class CustomModulesForm extends React.PureComponent { renderDropdownStartTime = () => { return (
- + this.setState({ endTime: e.target.value })} @@ -220,9 +209,9 @@ class CustomModulesForm extends React.PureComponent { renderDropdownLessonType = () => { return (
- + this.setState({ classNo: e.target.value })} placeholder="Class No" @@ -255,9 +244,9 @@ class CustomModulesForm extends React.PureComponent { renderInputVenue = () => { return (
- + this.setState({ venue: e.target.value })} placeholder="Venue" @@ -270,9 +259,9 @@ class CustomModulesForm extends React.PureComponent { renderDropdownDay = () => { return (
- +