Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Custom modules #2785

Open
wants to merge 48 commits into
base: master
Choose a base branch
from

Conversation

agnesnatasya
Copy link
Contributor

@agnesnatasya agnesnatasya commented Jul 26, 2020

Context

#2040
Feature request to add customised module to the timetable.

Implementation

I created a form that will require the user to input necessary information to create class of the customised module. The added class is supposed to appear in the timetable.
demo feature

This is the responsive design for several screen widths

  • Wide screen

Screenshot 2020-07-26 at 3 42 52 PM

  • Below md

Screenshot 2020-07-26 at 3 43 03 PM

  • Small screen

Screenshot 2020-07-26 at 3 43 15 PM

Other Information

This is my first PR, I encountered a problem but not really sure how to solve it. After the submit button is clicked, it dispatches the action, and in the log, the difference of the state is correct, but the page is just loading and it just hang in there. In the log, there is no log of waiting or doing for something, so I am not sure what did I do wrong.

If this problem has been rectified, I will modify the reducer, when it encounters the same module code and title, there are some checks that needs to be done.

Any help is appreciated, thank you!

Copy link
Member

@ZhangYiJiang ZhangYiJiang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job on your first PR!

The reason the page won't stop loading is because the final rendered timetable needs data from two places - the timetable config, which you have implemented, and the actual Module object loaded from the API and cached in module bank, which you have not. If the page finds a config but no module, it assumes the module is not loaded, and displays a spinner.

To fix this, you need to store the custom module's data in the module bank too. You will also need to persist this. I suggest adding a new customModules key to the settings reducer (which is already persisted) and creating a selector to merge that with the module bank.

Do also run the linter before pushing

onChange={(e) => this.props.onChangeVenue(e)}
placeholder="Venue."
/>
<Downshift onChange={this.onSelectDay}>{this.renderDropdownDay}</Downshift>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These dropdown menu should just be <select> elements - use Downshift only if you want comboboxes where there are too many options to select without searching. See AvailabilitySearch for an example of time selection. You might actually be able to reuse that component if you can make it more generic

$item-padding-vertical: 0.6rem;
$btn-margin: 0.5rem;

.formGroup {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use Bootstrap's form and layout system instead of trying to rebuild everything from scratch https://getbootstrap.com/docs/4.5/components/forms/

<input
className={classnames(styles.input, styles.titleIcon)}
onChange={(e) => this.setState({ moduleCode: e.target.value })}
placeholder="Module Code"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using the placeholder for label. Each form elements should have a proper label element associated with them (which can be hidden visually if needed)

};

isFormValid = () => {
const validity = Boolean(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try relying on HTML validation instead by adding required property to input elements

timestamp: number;
};

class CustomModulesForm extends React.Component<Props, State> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can, try creating functional components. At least make this a PureComponent for better performance

@codecov
Copy link

codecov bot commented Jul 29, 2020

Codecov Report

Attention: Patch coverage is 8.04598% with 80 lines in your changes missing coverage. Please review.

Project coverage is 45.34%. Comparing base (eb521e6) to head (a32aace).
Report is 639 commits behind head on master.

Files Patch % Lines
website/src/views/timetable/CustomModulesForm.tsx 4.25% 45 Missing ⚠️
website/src/actions/timetables.ts 4.34% 22 Missing ⚠️
...ite/src/views/timetable/CustomModulesContainer.tsx 0.00% 10 Missing ⚠️
website/src/reducers/moduleBank.ts 0.00% 2 Missing ⚠️
website/src/views/timetable/TimetableContent.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2785      +/-   ##
==========================================
- Coverage   45.95%   45.34%   -0.61%     
==========================================
  Files         248      250       +2     
  Lines        5273     5359      +86     
  Branches     1222     1234      +12     
==========================================
+ Hits         2423     2430       +7     
- Misses       2850     2929      +79     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@nusmods-deploy-bot
Copy link

nusmods-deploy-bot bot commented Jul 29, 2020

@agnesnatasya agnesnatasya marked this pull request as ready for review July 29, 2020 13:03
@agnesnatasya
Copy link
Contributor Author

agnesnatasya commented Jul 29, 2020

Hi @ZhangYiJiang thank you so much for your help and review. I have solved the loading problem by making moduleBank reducer process ADD_CUSTOM_MODULE action too. I have also implemented all of your review suggestions.

The UI is responsive, here is the display in larger screen, you can look at the other screen widths in the deployment preview.
Screenshot 2020-07-29 at 8 54 59 PM

This functionality is also available in the vertical mode.
Screenshot 2020-07-29 at 9 58 39 PM

Several cases that I considered:

  • If the user adds a custom module with same code as one of their custom modules, and the title is different, a notification will be displayed.
  • If the user adds a custom module with the same code and title as one of their custom modules, the classes willl be added to the Semester data
  • All custom module code added will be appended with an apostrophe, to indicate that it is a custom module

Thank you!

renderInputClassNo = () => {
return (
<div className="form-group">
<label htmlFor="module-code">Class No.</label>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDs should be unique. module-code might be a bit generic, so you want to prefix it with something, and here it is a copy-paste error. Also class no. can probably be generated automatically - they don't matter for custom modules

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I will add '-custom-module' to the end of it to ensure uniqueness. For class number, I think it is still useful for custom modules, there might be several tutorial classes, for example.

website/src/views/timetable/CustomModulesForm.tsx Outdated Show resolved Hide resolved
website/src/views/timetable/CustomModulesForm.tsx Outdated Show resolved Hide resolved
website/src/views/timetable/CustomModulesForm.tsx Outdated Show resolved Hide resolved
website/src/views/timetable/CustomModulesForm.tsx Outdated Show resolved Hide resolved
@ZhangYiJiang
Copy link
Member

Using the module code field to indicate a module is custom is probably a bad idea. You're using a single field to store two pieces of unrelated information, and that's only going to cause issues in the future. A better way would be to add a customModule?: boolean property to the Module type and use that instead.

The UI is going to take a bit of work. You probably need a way for the user to specify different lesson types. The horizontal mode UI is also a bit too wide - you can probably fit the lesson fields on one line, which would make it easier to specify additional lessons. You'll also need to consider UI for editing existing custom modules and removing them.

@agnesnatasya
Copy link
Contributor Author

Hi @ZhangYiJiang thank you so much for your review again, appreciate it.
The reason that I add apostrophe, ', in the code of the custom module, is to not confuse the user when they input the same code as a pre-existing module. For example, if someone puts CS1010 in the custom module, the name will be CS1010', so that it is still displayed as a different module from the original CS1010 module from NUSMods, so someone can have both in the timetable.

Also, it is to prevent custom module with the same code as a pre-existing module to overwrite the pre-existing module in the reducer. For example, in timetables reducer, if the moduleCode is the same, the old one will be overwritten, because it is a key-value pair in the dictionary.

    case ADD_MODULE:
    case ADD_CUSTOM_MODULE:
      return {
        ...state,
        [moduleCode]: action.payload.moduleLessonConfig,
      };

If I add a customModule? field in Module type, it still cannot handle this case because it is key-pair of dictionary.

May I clarify what do you mean by 'You probably need a way for the user to specify different lesson types'? There has been a 'lesson type' field in the form, or do I understand you wrongly?

Also, for the part 'you can probably fit the lesson fields on one line, which would make it easier to specify additional lessons', do you want the UI for horizontal mode on large screen or above, to be like

Module Code, Module Title and Module Credit in first line
Lesson Type, Class No, Venue, Day, Start Time, End Time in second line

?

Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants