{p.text}
41 | ))} 42 | 43 | )} 44 | action={actions} 45 | /> 46 | ); 47 | }; 48 | 49 | export default NoticeDisplay; 50 | -------------------------------------------------------------------------------- /src/actions/timetable.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'redux'; 2 | import { YearAndTerm, getCurrentTerm } from '../state'; 3 | import { SessionManagerData } from '../components/Timetable/SessionManagerTypes'; 4 | 5 | export const UPDATE_SESSION_MANAGER = 'UPDATE_SESSION_MANAGER'; 6 | 7 | export interface SessionManagerAction extends Action { 8 | type: typeof UPDATE_SESSION_MANAGER, 9 | sessionManager: SessionManagerData, 10 | term: string, 11 | } 12 | 13 | 14 | export const UPDATE_SUGGESTED_TIMETABLE = 'UPDATE_SUGGESTED_TIMETABLE'; 15 | 16 | export interface SuggestionAction extends Action { 17 | type: typeof UPDATE_SUGGESTED_TIMETABLE, 18 | score: number | null, 19 | } 20 | 21 | 22 | export const UPDATE_UNPLACED_COUNT = 'UPDATE_UNPLACED_COUNT'; 23 | 24 | export interface UnplacedCountAction extends Action { 25 | type: typeof UPDATE_UNPLACED_COUNT, 26 | count: number, 27 | } 28 | 29 | 30 | export function setTimetable( 31 | newTimetable: SessionManagerData, 32 | meta: YearAndTerm, 33 | ): SessionManagerAction { 34 | return { 35 | type: UPDATE_SESSION_MANAGER, 36 | sessionManager: newTimetable, 37 | term: getCurrentTerm(meta), 38 | }; 39 | } 40 | 41 | export function setSuggestionScore(score: number | null): SuggestionAction { 42 | return { 43 | type: UPDATE_SUGGESTED_TIMETABLE, 44 | score, 45 | }; 46 | } 47 | 48 | export function setUnplacedCount(count: number): UnplacedCountAction { 49 | return { 50 | type: UPDATE_UNPLACED_COUNT, 51 | count, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CrossAngles 2 | 3 | Welcome to CrossAngles! This is the official repository for the unofficial 4 | timetable planner. This repository is for the development of CrossAngles, 5 | if you're looking for the app to use it, you can find it at: 6 | https://crossangles.app 7 | 8 | ## Getting started 9 | 10 | To get started with developing locally, clone the repository, and install the 11 | dependencies using: 12 | 13 | ```bash 14 | yarn install 15 | ``` 16 | 17 | ## Running the app development server 18 | 19 | To build and serve the web app locally, you can use: 20 | 21 | ```bash 22 | yarn start 23 | ``` 24 | 25 | ## Running tests 26 | 27 | ```bash 28 | # Run the unit tests 29 | yarn test 30 | 31 | # Lint the code 32 | yarn lint 33 | ``` 34 | 35 | The unit tests use Jest, and linting is done with ESLint. 36 | 37 | ## Contributing 38 | 39 | If you wish to contribute you are very welcome to fork this repository and 40 | submit pull requests, but please refrain from distributing it or any 41 | derivatives. 42 | 43 | ### Adding tests and making changes 44 | When adding new features or touching existing code it is expected to add 45 | suitable test coverage for the code as well. Usually, for `example.ts`, the 46 | tests would go in `example.spec.ts`. 47 | 48 | ### Automated Testing 49 | This repository has automated testing set up, and it will run all the tests on 50 | the code before your PR can be merged. 51 | 52 | ### Code Review 53 | Another quality gate is the code review process. At least one person must review 54 | each pull-request before it can be merged. 55 | -------------------------------------------------------------------------------- /src/components/WebStream.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import makeStyles from '@material-ui/core/styles/makeStyles'; 4 | import FormControlLabel from '@material-ui/core/FormControlLabel'; 5 | import Checkbox from '@material-ui/core/Checkbox'; 6 | import { StreamData } from '../state'; 7 | import { getOptions } from '../state/selectors'; 8 | 9 | 10 | const useStyles = makeStyles(theme => ({ 11 | lessSpaceAbove: { 12 | marginTop: -theme.spacing(1), 13 | }, 14 | secondaryText: { 15 | color: theme.palette.text.secondary, 16 | }, 17 | })); 18 | 19 | export interface Props { 20 | checked: boolean, 21 | stream: StreamData, 22 | onChange: () => void, 23 | } 24 | 25 | function WebStream({ checked, stream, onChange }: Props) { 26 | const classes = useStyles(); 27 | const { darkMode, includeFull } = useSelector(getOptions); 28 | 29 | const disabled = stream.full && !includeFull; 30 | const descriptor = 'watch-later'; 31 | let label = `Choose ${descriptor} lecture stream`; 32 | if (stream.full) { 33 | label += ' (full)'; 34 | } 35 | 36 | return ( 37 |