From eae331b91f9a3bdf5389ff1cac1116f2d97e5bd5 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 15:51:36 -0700 Subject: [PATCH 01/31] Added root-level frontend README --- src/client/README.md | 11 +++++++++++ src/client/main.tsx | 1 + src/client/pages/README.md | 1 + src/client/routes.tsx | 18 +++++++++++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/client/README.md create mode 100644 src/client/pages/README.md diff --git a/src/client/README.md b/src/client/README.md new file mode 100644 index 00000000..f158a420 --- /dev/null +++ b/src/client/README.md @@ -0,0 +1,11 @@ +## src/client - Frontend + +This directory is the home for the frontend for tesc.events. + +tesc.events' frontend is written entirely in [React.js](https://reactjs.org/). It was initially written in JavaScript, but [was ported over to TypeScript in April 2019](https://github.com/UCSDTESC/Check-in/pull/131) + +The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/main.tsx) + +Routes for the application (/user/, /admin/ etc.) are set up in [routes.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/routes.tsx) + +The main application code lies in [pages/](https://github.com/UCSDTESC/Check-in/blob/master/src/client/pages) diff --git a/src/client/main.tsx b/src/client/main.tsx index 17f91144..3b0776ca 100644 --- a/src/client/main.tsx +++ b/src/client/main.tsx @@ -22,6 +22,7 @@ const store = createStore(reducer, applyMiddleware(reduxThunk as ThunkMiddleware) )); +//Configuring the application for routing, cookies, and redux. render( ( diff --git a/src/client/pages/README.md b/src/client/pages/README.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/client/pages/README.md @@ -0,0 +1 @@ + diff --git a/src/client/routes.tsx b/src/client/routes.tsx index edfa1a4d..91ff0028 100644 --- a/src/client/routes.tsx +++ b/src/client/routes.tsx @@ -13,18 +13,29 @@ import { authorised as AdminAuthorised } from '~/data/AdminApi'; import { authorised as UserAuthorised } from '~/data/UserApi'; import CookieTypes from '~/static/Cookies'; +/* + PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around + react-router-dom's Route component to handle authentication state. +*/ import PrivateRoute from './PrivateRoute'; import PrivateUserRoute from './PrivateUserRoute'; + +//Authentication Components & Actions +//TODO: Document better import { ApplicationDispatch } from './actions'; import AdminLogout from './auth/admin/Logout'; import { finishAuthorisation, authoriseAdmin, logoutAdmin } from './auth/admin/actions'; import ConfirmPage from './auth/user/Confirm'; import UserLogout from './auth/user/Logout'; import { authoriseUser, finishAuthorisation as finishUserAuth, logoutUser } from './auth/user/actions'; + +//Importing the different layouts (page structures) for the application import AdminLayout from './layouts/admin'; import SponsorLayout from './layouts/sponsor'; import PublicLayout from './layouts/public'; import UserLayout from './layouts/user'; + +//Importing all the pages for the app, used later when setting up routes. import AdminsPage from './pages/AdminsPage'; import ApplyPage from './pages/ApplyPage'; import CheckinPage from './pages/CheckinPage'; @@ -140,6 +151,12 @@ class Routes extends React.Component { ); } + /** + * Render a route with the User layout. + * @param {JSX.IntrinsicElements} component The child component to render within the + * layout. + * @returns {Component} + */ renderUser = (RenderComponent: any) => { return (props: RouteComponentProps) => ( @@ -213,7 +230,6 @@ class Routes extends React.Component { /> {/* User Routes */} - Date: Sat, 18 May 2019 16:19:35 -0700 Subject: [PATCH 02/31] Tree --- src/client/README.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/client/README.md b/src/client/README.md index f158a420..9961e775 100644 --- a/src/client/README.md +++ b/src/client/README.md @@ -6,6 +6,35 @@ tesc.events' frontend is written entirely in [React.js](https://reactjs.org/). I The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/main.tsx) -Routes for the application (/user/, /admin/ etc.) are set up in [routes.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/routes.tsx) -The main application code lies in [pages/](https://github.com/UCSDTESC/Check-in/blob/master/src/client/pages) +### Directory Tree +``` +├── PrivateRoute.tsx + * Wrapper Component around react-router-dom's Route to handle rendering only if the user requesting the page is authenticated. Used on admin-side routes. +├── PrivateUserRoute.tsx + * Wrapper Component around react-router-dom's Route to handle rendering only if the user requesting the page is authenticated. Used on user-side routes. +├── README.md + * 😊 +├── actions + * This directory holds application-level [Redux Actions](https://redux.js.org/basics/actions). +├── auth + * This directory holds the components, [Redux Actions](https://redux.js.org/basics/actions) and [Redux Reducers](https://redux.js.org/basics/reducers) related to the login/logout functionality of both users and admins. +├── components + * Components that are required "globally" or in multiple places in the application are put here. Things like the Navbar, Footer, iOS Switch, Loading spinners etc. go here. +├── data + * The `data/` directory holds `Api.ts` and `User.ts`, which provide the application with methods to make API calls to our backend. +├── layouts + * A Layout defines the way our application looks. The application has different layouts depending on what kind of page you are looking at - admins and sponsors have sidebars, and users dont. Layouts are linked to a specific route in the `routes.tsx` file. +├── main.tsx + * This is the entrypoint to our application. It sets up our app to be used with Redux, react-router and Cookies and makes the intial React.Component.render() call. +├── pages + * Each `XYZPage` in the `pages` directory is linked to a specific page of the app. Each page is it’s own directory with an `index.tsx` file in it that defines that page. Each page can also define Redux Actions, Reducers and Components that it will use in `XYZPage/actions`, `XYZPage/reducers` and `XYZPage/components +├── reducers + * This directory holds application-level [Redux Reducers](https://redux.js.org/basics/reducers). +├── routes.tsx + * This directory defines react-router-dom's routes for the application. +├── static + * This directory holds "constant" data that the application needs - a list of universities, majors and so on. +└── typings + * TypeScript type definitions for JavaScript packages used in this application that are directly supplied by us - not by node_modules +``` \ No newline at end of file From 5b9d6f143603ddc7131726c89c094ce8120d209a Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 16:23:48 -0700 Subject: [PATCH 03/31] Removed links --- src/client/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/README.md b/src/client/README.md index 9961e775..e9e90964 100644 --- a/src/client/README.md +++ b/src/client/README.md @@ -16,9 +16,9 @@ The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-i ├── README.md * 😊 ├── actions - * This directory holds application-level [Redux Actions](https://redux.js.org/basics/actions). + * This directory holds application-level Redux Actions. ├── auth - * This directory holds the components, [Redux Actions](https://redux.js.org/basics/actions) and [Redux Reducers](https://redux.js.org/basics/reducers) related to the login/logout functionality of both users and admins. + * This directory holds the components, Redux Actions and Redux Reducers related to the login/logout functionality of both users and admins. ├── components * Components that are required "globally" or in multiple places in the application are put here. Things like the Navbar, Footer, iOS Switch, Loading spinners etc. go here. ├── data @@ -30,7 +30,7 @@ The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-i ├── pages * Each `XYZPage` in the `pages` directory is linked to a specific page of the app. Each page is it’s own directory with an `index.tsx` file in it that defines that page. Each page can also define Redux Actions, Reducers and Components that it will use in `XYZPage/actions`, `XYZPage/reducers` and `XYZPage/components ├── reducers - * This directory holds application-level [Redux Reducers](https://redux.js.org/basics/reducers). + * This directory holds application-level Redux Reducers. ├── routes.tsx * This directory defines react-router-dom's routes for the application. ├── static From f63cf7e3dd4614f012945c0df5865ef2979fc9aa Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 17:19:25 -0700 Subject: [PATCH 04/31] Added pages documentation --- src/client/pages/README.md | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/client/pages/README.md b/src/client/pages/README.md index 8b137891..2c3375c2 100644 --- a/src/client/pages/README.md +++ b/src/client/pages/README.md @@ -1 +1,43 @@ +## src/client/pages - Pages. Pages Everywhere. +An `XYZPage` in this directory implements a page in the application. Any `XYZPage` can define redux actions in `XYZPage/actions`, reducers in `XYZPage/reducers` or components it uses in `XYZPage/components`. + +``` +├── AdminsPage + * [Admin with role Developer only] It shows a list of all admins in the system. +├── AlertPage.tsx + * This is an abstraction that allows a page to have "alerts" at the top of it. An `XYZPage` can extend this class to implement alerts. +├── ApplyPage + * This is the hackathon registration page. +├── CheckinPage + * [Admin only] This page is the QR Code Checkin system for checkin into an event. +├── DashboardPage + * [Admin only] This page is the "opening" dashboard for an admin when they log into the system. + * It decides what to show to the admin depending on their role and the API response +├── EventPage + * [Admin only] This is the main dashboard for an event. It has tabs (extends `TabularPage.tsx`) for Actions, Administrators, Settings and Statistics +├── ForgotPage.tsx + * This page implements the forgot password functionality for users. +├── HomePage + * This is the main page that loads when a user goes to www.tesc.events + * This page handles 2 states + - showing all apply-able events when the user is not logged in, and show + - showing all existing applications when the user is logged in +├── LoginPage.tsx + * This page shows a username and password field for a user to login. +├── NewEventPage + * [Admin only] This page allows an admin to create a new event. +├── NotFound.tsx + * This is the 404 page. react-router is set up to show this when none of the routes are rendered. +├── ResetPage.tsx + * [User only] This page allows a user to reset their password (linked from a password reset email) +├── ResumesPage + * [Admin only] This is the sponsor-tool. It provides a dashboard with sorting / filtering features that is given to sponsors who pay for access to tesc.events +├── TabularPage.tsx + * This is an abstraction that lets a page that extends it implement tabs. +├── UserPage + * [User only] This is the page that lets a user view / edit their hackathon application +└── UsersPage + * [Admin only] This is the page that shows admins a list of users that have applied to an event and lets them update the application if needed. + * This page also has extensive sorting features for admins. +``` \ No newline at end of file From af31bfe7d64dff305122972449ae5985b053536f Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 19:07:45 -0700 Subject: [PATCH 05/31] Documentation for AdminsPage and ApplyPage done --- src/client/pages/AdminsPage/actions/index.ts | 4 ++- .../pages/AdminsPage/components/AdminList.tsx | 7 +++++ src/client/pages/AdminsPage/index.tsx | 30 +++++++++++++++++++ .../pages/AdminsPage/reducers/Admins.ts | 1 + src/client/pages/AlertPage.tsx | 12 +++++++- .../ApplyPage/components/ApplyPageSection.tsx | 9 ++++++ .../pages/ApplyPage/components/Header.tsx | 6 ++++ .../ApplyPage/components/PersonalSection.tsx | 18 +++++++++++ .../ApplyPage/components/ResponseSection.tsx | 7 ++++- .../ApplyPage/components/UniversityField.tsx | 6 ++++ src/client/pages/ApplyPage/index.tsx | 24 +++++++++++++++ src/client/pages/README.md | 1 + 12 files changed, 122 insertions(+), 3 deletions(-) diff --git a/src/client/pages/AdminsPage/actions/index.ts b/src/client/pages/AdminsPage/actions/index.ts index 10dc9065..ad5d28ae 100644 --- a/src/client/pages/AdminsPage/actions/index.ts +++ b/src/client/pages/AdminsPage/actions/index.ts @@ -3,6 +3,8 @@ import { createStandardAction } from 'typesafe-actions'; import * as Types from './types'; -// Admins +// TODO: Not sure what this action does export const addAdmins = createStandardAction(Types.ADD_ADMINS)(); + +// Replace the `admins` state with the payload export const replaceAdmins = createStandardAction(Types.REPLACE_ADMINS)(); diff --git a/src/client/pages/AdminsPage/components/AdminList.tsx b/src/client/pages/AdminsPage/components/AdminList.tsx index 31370393..61df617b 100644 --- a/src/client/pages/AdminsPage/components/AdminList.tsx +++ b/src/client/pages/AdminsPage/components/AdminList.tsx @@ -4,8 +4,15 @@ import React from 'react'; import { Button } from 'reactstrap'; interface AdminListProps { + + //the admins in the system admins: Admin[]; + + //function to be called when the delete button is pressed onDeleteAdmin: (adminId: string) => void; + + //is the admin in editing mode? + // TODO: remove - legacy feature editing: boolean; } diff --git a/src/client/pages/AdminsPage/index.tsx b/src/client/pages/AdminsPage/index.tsx index db6b392d..afcd9319 100644 --- a/src/client/pages/AdminsPage/index.tsx +++ b/src/client/pages/AdminsPage/index.tsx @@ -24,17 +24,35 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface AdminsPageProps { } +/** + * This component receives props in 3 ways - + * 1) The explicit props provied to it by AdminsPageProps + * 2) The redux state provided to it by mapStateToProps + * 3) The dispatch functions provided to it by mapDispatchToProps + * + * So, the props of this component is the union of the return types of mapStateToProps, + * mapDispatchToProps and AdminsPageProps + */ type Props = ReturnType & ReturnType & AdminsPageProps; interface AdminsPageState { + + //tracks whether the New Admin Modal is open or not isRegisterModalOpen: boolean; } +/** + * This pageshows a list of all admins in the system. It also can create and delete admins. + * This page is locked to users with the Developer role (The logic for this is in AdminLayout). + */ class AdminsPage extends React.Component { state: Readonly = { isRegisterModalOpen: false, }; + /** + * Get admins from the backend and put them into the redux state. + */ loadAdmins = () => loadAllAdmins() .then(res => this.props.replaceAdmins(res)) @@ -43,14 +61,22 @@ class AdminsPage extends React.Component { componentDidMount() { this.props.showLoading(); + //Hide loading state after the API returns the admins this.loadAdmins() .then(() => this.props.hideLoading()); } + /** + * Toggle the isRegisterModalOpen state variable to show or hide new admin modal + */ toggleRegisterModal = () => this.setState({ isRegisterModalOpen: !this.state.isRegisterModalOpen, }); + /** + * Create a new admin in the database and update the frontend to reflect the change + * @param {NewAdminModalFormData} newAdmin the new admin to be added to the system + */ registerNewAdmin = (newAdmin: NewAdminModalFormData) => { registerAdmin(newAdmin) .then(this.loadAdmins) @@ -58,6 +84,10 @@ class AdminsPage extends React.Component { .catch(console.error); } + /** + * Delete an admin from the system, and update the frontend to reflect the change + * @param {String} adminId the admin to be deleted from the system + */ onDeleteAdmin = (adminId: string) => deleteAdmin(adminId) .then(this.loadAdmins) diff --git a/src/client/pages/AdminsPage/reducers/Admins.ts b/src/client/pages/AdminsPage/reducers/Admins.ts index e86d7639..6cf69dcd 100644 --- a/src/client/pages/AdminsPage/reducers/Admins.ts +++ b/src/client/pages/AdminsPage/reducers/Admins.ts @@ -7,6 +7,7 @@ import * as Types from '../actions/types'; const initialState: Admin[] = []; +//Tell redux what to do when it sees ADD_ADMINS and REPLACE_ADMINS export default handleActions({ [Types.ADD_ADMINS]: (state, action: ActionType) => ([ ...state, diff --git a/src/client/pages/AlertPage.tsx b/src/client/pages/AlertPage.tsx index 1b85094f..f2347792 100644 --- a/src/client/pages/AlertPage.tsx +++ b/src/client/pages/AlertPage.tsx @@ -35,6 +35,7 @@ export const AlertPageAbove: React.StatelessComponent = (props) => { * Allows for extension with bootstrap alerts in the state. */ export default class AlertPage extends React.Component { + /** * Creates a new alert to render to the top of the screen. * @param {String} message The message to display in the alert. @@ -52,7 +53,7 @@ export default class AlertPage extends React.Compon } /** - * Creates a new error alert if there was a login error. + * Creates a new error alert if there was an error. * @param {PageAlert} alert The alert to display. * @param {String} key The given key for the element map. * @param {Boolean} container Determines whether the alert is wrapped in a container. @@ -75,12 +76,21 @@ export default class AlertPage extends React.Compon } } + /** + * Empties the alerts in the state. + */ clearAlerts = () => { this.setState({ alerts: [], }); }; + /** + * Show the alerts currently in state. + * @param {Boolean} container Determines whether the alert is wrapped in a container. + * @param {className} Override the alert with a different className. + * @returns {Component} + */ renderAlerts = (container: boolean = false, className: string = 'alert-page__alert') => { return ( diff --git a/src/client/pages/ApplyPage/components/ApplyPageSection.tsx b/src/client/pages/ApplyPage/components/ApplyPageSection.tsx index f2ecf52f..d5c15a4c 100644 --- a/src/client/pages/ApplyPage/components/ApplyPageSection.tsx +++ b/src/client/pages/ApplyPage/components/ApplyPageSection.tsx @@ -3,10 +3,19 @@ import React from 'react'; import { InjectedFormProps } from 'redux-form'; export interface ApplyPageSectionProps { + + //The event this application is for event: TESCEvent; + + //function to be called when the back button is pressed goToPreviousPage?: () => void; } +/** + * This is an abstraction that creates a React component with the props for + * the current event being applied to and a function that makes the ApplyPage + * move the page. Each section of the application simply extends this class. + */ export default class ApplyPageSection extends React.Component

, S> { diff --git a/src/client/pages/ApplyPage/components/Header.tsx b/src/client/pages/ApplyPage/components/Header.tsx index 6d7c418b..c31a7586 100644 --- a/src/client/pages/ApplyPage/components/Header.tsx +++ b/src/client/pages/ApplyPage/components/Header.tsx @@ -2,8 +2,14 @@ import { Logo } from '@Shared/ModelTypes'; import React from 'react'; interface HeaderProps { + + //Name of the event name: string; + + //Logo of the event logo: Logo; + + //Description of the event description: string; } diff --git a/src/client/pages/ApplyPage/components/PersonalSection.tsx b/src/client/pages/ApplyPage/components/PersonalSection.tsx index aeeb5ef3..6e4b56b7 100644 --- a/src/client/pages/ApplyPage/components/PersonalSection.tsx +++ b/src/client/pages/ApplyPage/components/PersonalSection.tsx @@ -9,7 +9,11 @@ import ApplyPageSection, { ApplyPageSectionProps } from './ApplyPageSection'; import UniversityField from './UniversityField'; interface PersonalSectionProps extends ApplyPageSectionProps { + + //function to be called when the email changes, to check if the user exists in the database onEmailChange: (newEmail: string) => void; + + //the current event being applied to event: TESCEvent; } @@ -19,6 +23,9 @@ export enum InstitutionType { HighSchool = 'hs', } +/** + * Override defined form data to track the data in the way the UI shows it. + */ export interface PersonalSectionFormData extends RegisterUserPersonalSectionRequest { birthdateMonth: number; birthdateDay: number; @@ -28,6 +35,13 @@ export interface PersonalSectionFormData extends RegisterUserPersonalSectionRequ } class PersonalSection extends ApplyPageSection { + + + /** + * Create the email component of the application. + * Use onBlur to check if the email exists when the user takes their focus off this field. + * @returns {Component} + */ createEmailField() { return ( ; } - // TODO: Make into a statically-typed method + /** + * Render the custom questions for this event, given the type of question to be rendered + * TODO: Make into a statically-typed method + * @param {CustomQuestions} customQuestions the custom questions of this event + * @param {QuestionType} type The type of question to be rendered + */ renderCustomQuestions(customQuestions: CustomQuestions, type: QuestionType) { let inputField: (fieldName: string, value: any, ...otherArgs: any[]) => JSX.Element | JSX.Element[] = null; diff --git a/src/client/pages/ApplyPage/components/UniversityField.tsx b/src/client/pages/ApplyPage/components/UniversityField.tsx index 2e2ed6f0..ec0fecd1 100644 --- a/src/client/pages/ApplyPage/components/UniversityField.tsx +++ b/src/client/pages/ApplyPage/components/UniversityField.tsx @@ -12,6 +12,12 @@ interface UniversityFieldProps { type Props = WrappedFieldProps & UniversityFieldProps; export default class UniversityField extends React.Component { + + /** + * Function that is called when the user selects a suggestion from the AutoSuggest + * TODO: better documentation + * @param {String} suggestion The suggestion that the user selected + */ onUniversitySelected = (suggestion: string) => { const {input} = this.props; input.onChange(suggestion); diff --git a/src/client/pages/ApplyPage/index.tsx b/src/client/pages/ApplyPage/index.tsx index 18488f4e..3aeb5b07 100644 --- a/src/client/pages/ApplyPage/index.tsx +++ b/src/client/pages/ApplyPage/index.tsx @@ -19,19 +19,34 @@ interface ApplyPageProps { } type Props = ApplyPageProps & RouteComponentProps<{ + //eventAlias for the event this application is for eventAlias: string; }>; interface ApplyPageState { + + //tracks which application page the user is on page: number; + + //tracks application error error: Error; + + //tracks if the user has yet to submit the application isSubmitting: boolean; + + //the event this application is for event: TESCEvent; + + //does the user have an exisiting tesc.events account? emailExists: boolean; } +//The complete application is the union of data from its 3 pages export type ApplyPageFormData = PersonalSectionFormData & ResponseSectionFormData & UserSectionFormData; +/** + * This page is the application for an event. It implements a multi page form. + */ class ApplyPage extends React.Component { state: Readonly = { page: 1, @@ -88,7 +103,13 @@ class ApplyPage extends React.Component { this.loadPageFromHash(); } + /** + * Sanitise user input, used before the final submit. + * @param {ApplyPageFormData} values the user's application + */ sanitiseValues(values: ApplyPageFormData) { + + //parse date numbers into a datestring values.birthdate = new Date( values.birthdateYear, values.birthdateMonth - 1, @@ -130,6 +151,7 @@ class ApplyPage extends React.Component { return; } + //checks if user exists with an API call checkUserExists(email) .then((ret) => { this.setState({ @@ -152,6 +174,7 @@ class ApplyPage extends React.Component { isSubmitting: true, }); + //Send Application to backend registerUser(this.props.match.params.eventAlias, values) .then(() => { // Log successful application with Google Analytics @@ -160,6 +183,7 @@ class ApplyPage extends React.Component { action: 'Successful', }); + //Show success page this.nextPage(); }) .catch((err) => { diff --git a/src/client/pages/README.md b/src/client/pages/README.md index 2c3375c2..d0b77308 100644 --- a/src/client/pages/README.md +++ b/src/client/pages/README.md @@ -2,6 +2,7 @@ An `XYZPage` in this directory implements a page in the application. Any `XYZPage` can define redux actions in `XYZPage/actions`, reducers in `XYZPage/reducers` or components it uses in `XYZPage/components`. +# Directory Tree ``` ├── AdminsPage * [Admin with role Developer only] It shows a list of all admins in the system. From aa5093430ffea6341027245cc70a486344d3a9eb Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 21:39:12 -0700 Subject: [PATCH 06/31] DashboardPage finished, TODO SettingsTab + StatisticsTab --- .../components/AdminDashboard.tsx | 4 +++ .../DashboardPage/components/EventList.tsx | 6 ++++ .../components/SponsorDashboard.tsx | 2 ++ src/client/pages/DashboardPage/index.tsx | 15 ++++++++ src/client/pages/EventPage/index.tsx | 17 +++++++++ .../pages/EventPage/tabs/ActionsTab.tsx | 35 ++++++++++--------- .../EventPage/tabs/AdministratorsTab.tsx | 13 +++++++ .../pages/EventPage/tabs/EventPageTab.tsx | 4 +++ 8 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/client/pages/DashboardPage/components/AdminDashboard.tsx b/src/client/pages/DashboardPage/components/AdminDashboard.tsx index 00487143..20f18aa5 100644 --- a/src/client/pages/DashboardPage/components/AdminDashboard.tsx +++ b/src/client/pages/DashboardPage/components/AdminDashboard.tsx @@ -6,7 +6,11 @@ import React from 'react'; import EventList from './EventList'; interface AdminDashboardProps { + + //events that the admin is permitted to see events: TESCEvent[]; + + //The current user (aka admin) requesting the page user: JWTAdminAuthToken; } diff --git a/src/client/pages/DashboardPage/components/EventList.tsx b/src/client/pages/DashboardPage/components/EventList.tsx index f098b2a1..a052cbba 100644 --- a/src/client/pages/DashboardPage/components/EventList.tsx +++ b/src/client/pages/DashboardPage/components/EventList.tsx @@ -5,8 +5,14 @@ import { Link } from 'react-router-dom'; import EventCard from '~/components/EventCard'; interface EventListProps { + + //events to be rendered in the list events: TESCEvent[]; + + //track if we need a direct link to the resume page resumeLink?: boolean; + + //can this user create an event? canCreate?: boolean; } diff --git a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx index d14db74e..f62a5957 100644 --- a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx +++ b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx @@ -4,6 +4,8 @@ import React from 'react'; import EventList from './EventList'; interface SponsorDashboardProps { + + //events that this sponsor is allowed to see events: TESCEvent[]; } diff --git a/src/client/pages/DashboardPage/index.tsx b/src/client/pages/DashboardPage/index.tsx index 46169aa6..42d0049b 100644 --- a/src/client/pages/DashboardPage/index.tsx +++ b/src/client/pages/DashboardPage/index.tsx @@ -24,12 +24,27 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface DashboardPageProps { } +/** + * This component receives props in 3 ways - + * 1) The explicit props provied to it by DashboardPageProps + * 2) The redux state provided to it by mapStateToProps + * 3) The dispatch functions provided to it by mapDispatchToProps + * + * So, the props of this component is the union of the return types of mapStateToProps, + * mapDispatchToProps and DashboardPageProps + */ type Props = ReturnType & ReturnType & DashboardPageProps; +/** + * This is the page that an admin sees when they first log into tesc.events. + * It shows the list of events that admin is permitted to see + */ class DashboardPage extends React.Component { + componentDidMount() { this.props.showLoading(); + //hide the loading state at the end of this promise this.props.loadAllAdminEvents() .catch(console.error) .finally(this.props.hideLoading); diff --git a/src/client/pages/EventPage/index.tsx b/src/client/pages/EventPage/index.tsx index a7c64c65..ff8c9e91 100644 --- a/src/client/pages/EventPage/index.tsx +++ b/src/client/pages/EventPage/index.tsx @@ -29,6 +29,8 @@ import EventForm, { EventFormData } from '../../components/EventForm'; import createValidator from '../NewEventPage/validate'; type RouteProps = RouteComponentProps<{ + + //the eventAlias for the event we want to render the dashboard for eventAlias: string; }>; @@ -59,6 +61,15 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface EventPageProps extends TabularPageProps { } +/** + * This component receives props in 3 ways - + * 1) The explicit props provied to it by EventPageProps + * 2) The redux state provided to it by mapStateToProps + * 3) The dispatch functions provided to it by mapDispatchToProps + * + * So, the props of this component is the union of the return types of mapStateToProps, + * mapDispatchToProps and EventPageProps + */ export type Props = RouteProps & ReturnType & ReturnType & EventPageProps; @@ -66,6 +77,12 @@ interface EventPageState extends TabularPageState { teams: TESCTeam[]; } +/** + * This page renders the main page for an event. + * It has tabs and links to the other pages related to this event. + * + * This component is extending from TabularPage, which has the tabbing functionality abstracted away + */ class EventPage extends TabularPage { tabPages: Readonly = [ { diff --git a/src/client/pages/EventPage/tabs/ActionsTab.tsx b/src/client/pages/EventPage/tabs/ActionsTab.tsx index b1887184..50018aa3 100644 --- a/src/client/pages/EventPage/tabs/ActionsTab.tsx +++ b/src/client/pages/EventPage/tabs/ActionsTab.tsx @@ -8,26 +8,24 @@ import EventPageTab from './EventPageTab'; interface ActionsTabProps { } +/** + * ActionsTab holds the functionality for actions related to the event + * This tab currently has: + * - Export All Users As CSV + * - Bulk Status Change of multiple users by user IDs + */ export default class ActionsTab extends EventPageTab { + + /** + * This function is called when the user clicks the 'Export All Users' button. + */ exportUsers = () => { const eventAlias = this.props.event.alias; - exportUsers(eventAlias, false) - .end((err, res) => { - // Download as file - const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.setAttribute('download', `${eventAlias}-${Date.now()}.csv`); - document.body.appendChild(link); - - link.click(); - }); - } - exportEmails = () => { - const eventAlias = this.props.event.alias; - exportUsers(eventAlias, true) + //call API's exportUsers. + //surprisingly, this is okay syntax because exportUsers and this.exportUsers are different + //TODO: name API call better + exportUsers(eventAlias, false) .end((err, res) => { // Download as file const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' }); @@ -41,6 +39,10 @@ export default class ActionsTab extends EventPageTab { }); } + /** + * This function is called when the Bulk Change button is clicked on the form. + * @param {BulkChangeFormData} values the values of the Bulk Change Form + */ onBulkChange = (values: BulkChangeFormData) => { const { event } = this.props; const { users, status } = values; @@ -48,6 +50,7 @@ export default class ActionsTab extends EventPageTab { // Split users into array const usersSplit = users.split(/\n/); + //call API's bulk change bulkChange(usersSplit, status) .then(() => { this.props.addEventSuccessAlert(event.alias, 'Successfully updated users!', 'Bulk Change'); diff --git a/src/client/pages/EventPage/tabs/AdministratorsTab.tsx b/src/client/pages/EventPage/tabs/AdministratorsTab.tsx index 24c4a5a9..d64b82f8 100644 --- a/src/client/pages/EventPage/tabs/AdministratorsTab.tsx +++ b/src/client/pages/EventPage/tabs/AdministratorsTab.tsx @@ -12,10 +12,23 @@ interface AdministratorsTabProps { } interface AdminReference { + + //the database ID of an admin _id: string; + + //the username of an admin username: string; } +/** + * View Administrators in this event + * + * This tab currently has: + * - View current organizers + * - View current sponsors + * - Create a new sponsor + * - Create a new organizer + */ export default class AdministratorsTab extends EventPageTab { /** * Parses from a react-select element into an admin object. diff --git a/src/client/pages/EventPage/tabs/EventPageTab.tsx b/src/client/pages/EventPage/tabs/EventPageTab.tsx index 926504d3..92f8ba90 100644 --- a/src/client/pages/EventPage/tabs/EventPageTab.tsx +++ b/src/client/pages/EventPage/tabs/EventPageTab.tsx @@ -7,5 +7,9 @@ interface EventPageTabProps { event: TESCEvent; } +/** + * This is an abstraction that provides every EventPage tab with access to the event directly. + * This means that we don't have to explicitly tell every tab which event we are currently on. + */ export default class EventPageTab extends React.Component

{ } From 7084fa3c85bf60e161b91d389f6940443fa55b7e Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 15:00:58 -0700 Subject: [PATCH 07/31] Working through DashboardPage/components --- .../pages/AdminsPage/components/AdminList.tsx | 3 ++ .../pages/ApplyPage/components/Header.tsx | 3 ++ .../ApplyPage/components/PersonalSection.tsx | 4 +- .../ApplyPage/components/ResponseSection.tsx | 3 ++ .../ApplyPage/components/SubmittedSection.tsx | 3 ++ .../ApplyPage/components/UniversityField.tsx | 3 ++ .../ApplyPage/components/UserSection.tsx | 3 ++ src/client/pages/ApplyPage/index.tsx | 2 +- .../components/AdminDashboard.tsx | 3 ++ .../DashboardPage/components/EventList.tsx | 4 ++ .../components/SponsorDashboard.tsx | 4 ++ .../pages/EventPage/components/BulkChange.tsx | 8 ++++ .../components/CheckinStatistics.tsx | 3 ++ .../EventPage/components/CustomQuestion.tsx | 10 +++++ .../pages/EventPage/tabs/SettingsTab.tsx | 37 ++++++++++++++++++- .../pages/EventPage/tabs/StatisticsTab.tsx | 6 +++ 16 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/client/pages/AdminsPage/components/AdminList.tsx b/src/client/pages/AdminsPage/components/AdminList.tsx index 61df617b..ff6d54cd 100644 --- a/src/client/pages/AdminsPage/components/AdminList.tsx +++ b/src/client/pages/AdminsPage/components/AdminList.tsx @@ -26,6 +26,9 @@ interface AdminListState { columns: DisplayColumnMap; } +/** + * This component renders a list of admins on the admins page + */ export default class AdminList extends React.Component { state: Readonly = { columns: { diff --git a/src/client/pages/ApplyPage/components/Header.tsx b/src/client/pages/ApplyPage/components/Header.tsx index c31a7586..9c068656 100644 --- a/src/client/pages/ApplyPage/components/Header.tsx +++ b/src/client/pages/ApplyPage/components/Header.tsx @@ -13,6 +13,9 @@ interface HeaderProps { description: string; } +/** + * This is the header for the application. It shows the event name, description and logo. + */ export default class Header extends React.Component { render() { const {name, logo, description} = this.props; diff --git a/src/client/pages/ApplyPage/components/PersonalSection.tsx b/src/client/pages/ApplyPage/components/PersonalSection.tsx index 6e4b56b7..4d7a7b39 100644 --- a/src/client/pages/ApplyPage/components/PersonalSection.tsx +++ b/src/client/pages/ApplyPage/components/PersonalSection.tsx @@ -34,9 +34,11 @@ export interface PersonalSectionFormData extends RegisterUserPersonalSectionRequ resume?: File[]; } +/** + * This is the first page of the event application. This handles the user's personal details. + */ class PersonalSection extends ApplyPageSection { - /** * Create the email component of the application. * Use onBlur to check if the email exists when the user takes their focus off this field. diff --git a/src/client/pages/ApplyPage/components/ResponseSection.tsx b/src/client/pages/ApplyPage/components/ResponseSection.tsx index 3847f2b3..39d9b103 100644 --- a/src/client/pages/ApplyPage/components/ResponseSection.tsx +++ b/src/client/pages/ApplyPage/components/ResponseSection.tsx @@ -21,6 +21,9 @@ export interface ResponseSectionFormData extends RegisterUserResponseSectionRequ city?: string; } +/** + * This is the second page of the application. It handles the user's response to event-specific questions + */ class ResponseSection extends ApplyPageSection { state: Readonly = { teamState: undefined, diff --git a/src/client/pages/ApplyPage/components/SubmittedSection.tsx b/src/client/pages/ApplyPage/components/SubmittedSection.tsx index 6e3d8618..5bef7274 100644 --- a/src/client/pages/ApplyPage/components/SubmittedSection.tsx +++ b/src/client/pages/ApplyPage/components/SubmittedSection.tsx @@ -6,6 +6,9 @@ import ApplyPageSection, { ApplyPageSectionProps } from './ApplyPageSection'; interface SubmittedSectionProps extends ApplyPageSectionProps { } +/** + * This is the application success page + */ class SubmittedSection extends ApplyPageSection<{}, SubmittedSectionProps> { renderTeamCode = (info: WrappedFieldProps) => (

diff --git a/src/client/pages/ApplyPage/components/UniversityField.tsx b/src/client/pages/ApplyPage/components/UniversityField.tsx index ec0fecd1..c6cf3e85 100644 --- a/src/client/pages/ApplyPage/components/UniversityField.tsx +++ b/src/client/pages/ApplyPage/components/UniversityField.tsx @@ -11,6 +11,9 @@ interface UniversityFieldProps { type Props = WrappedFieldProps & UniversityFieldProps; +/** + * This component creates the university picker. It is used in ./PersonalSection.tsx + */ export default class UniversityField extends React.Component { /** diff --git a/src/client/pages/ApplyPage/components/UserSection.tsx b/src/client/pages/ApplyPage/components/UserSection.tsx index 0c8d75de..239379e7 100644 --- a/src/client/pages/ApplyPage/components/UserSection.tsx +++ b/src/client/pages/ApplyPage/components/UserSection.tsx @@ -18,6 +18,9 @@ export interface UserSectionFormData { confirmPassword?: string; } +/** + * This is the 3rd page of the application. It handles tesc.events account creation and MLH provisions + */ class UserSection extends ApplyPageSection { /** * Create a checkbox to accept the Code of Conduct. diff --git a/src/client/pages/ApplyPage/index.tsx b/src/client/pages/ApplyPage/index.tsx index 3aeb5b07..e60ff3f9 100644 --- a/src/client/pages/ApplyPage/index.tsx +++ b/src/client/pages/ApplyPage/index.tsx @@ -45,7 +45,7 @@ interface ApplyPageState { export type ApplyPageFormData = PersonalSectionFormData & ResponseSectionFormData & UserSectionFormData; /** - * This page is the application for an event. It implements a multi page form. + * This page is the application for an event. It implements a multi page application form. */ class ApplyPage extends React.Component { state: Readonly = { diff --git a/src/client/pages/DashboardPage/components/AdminDashboard.tsx b/src/client/pages/DashboardPage/components/AdminDashboard.tsx index 20f18aa5..fbb8fc6c 100644 --- a/src/client/pages/DashboardPage/components/AdminDashboard.tsx +++ b/src/client/pages/DashboardPage/components/AdminDashboard.tsx @@ -14,6 +14,9 @@ interface AdminDashboardProps { user: JWTAdminAuthToken; } +/** + * This is the admin dashboard. It is primarily a wrapper around EventList for now. + */ export default class AdminDashboard extends React.Component { render() { diff --git a/src/client/pages/DashboardPage/components/EventList.tsx b/src/client/pages/DashboardPage/components/EventList.tsx index a052cbba..cb255369 100644 --- a/src/client/pages/DashboardPage/components/EventList.tsx +++ b/src/client/pages/DashboardPage/components/EventList.tsx @@ -16,6 +16,10 @@ interface EventListProps { canCreate?: boolean; } +/** + * This component renders the cards for events on DashboardPage. + * It also has an "Add Event" button if the user is capable of user creation. + */ export default class EventList extends React.Component { render() { const { events, resumeLink, canCreate } = this.props; diff --git a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx index f62a5957..0a44a886 100644 --- a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx +++ b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx @@ -9,6 +9,10 @@ interface SponsorDashboardProps { events: TESCEvent[]; } +/** + * This is the sponsor's event list. It uses EventList's resumeLink prop to + * enforce a link to the sponsor portal on clicking the card. + */ export default class SponsorDashboard extends React.Component { render() { const {events} = this.props; diff --git a/src/client/pages/EventPage/components/BulkChange.tsx b/src/client/pages/EventPage/components/BulkChange.tsx index aefa7b4d..6579a7c6 100644 --- a/src/client/pages/EventPage/components/BulkChange.tsx +++ b/src/client/pages/EventPage/components/BulkChange.tsx @@ -3,7 +3,11 @@ import React from 'react'; import { Field, reduxForm, InjectedFormProps } from 'redux-form'; export interface BulkChangeFormData { + + // new line separated users: string; + + //new status to be set status: string; } @@ -11,8 +15,12 @@ interface BulkChangeProps { } +//Use a union of BulkChangeProps and Redux Form's Props type Props = InjectedFormProps & BulkChangeProps; +/** + * This component implements a feature to update multiple users' statuses by their user IDs + */ class BulkChange extends React.Component { render() { const {handleSubmit, pristine, submitting} = this.props; diff --git a/src/client/pages/EventPage/components/CheckinStatistics.tsx b/src/client/pages/EventPage/components/CheckinStatistics.tsx index 22f6ac55..daacfc79 100644 --- a/src/client/pages/EventPage/components/CheckinStatistics.tsx +++ b/src/client/pages/EventPage/components/CheckinStatistics.tsx @@ -3,6 +3,9 @@ import { Link } from 'react-router-dom'; import EventStatisticsComponent from './EventStatisticsComponent'; +/** + * This component shows the number of checkedin users in the header of the page + */ export default class CheckinStatistics extends EventStatisticsComponent { render() { const {event, statistics} = this.props; diff --git a/src/client/pages/EventPage/components/CustomQuestion.tsx b/src/client/pages/EventPage/components/CustomQuestion.tsx index d4ac013f..3b9ced6e 100644 --- a/src/client/pages/EventPage/components/CustomQuestion.tsx +++ b/src/client/pages/EventPage/components/CustomQuestion.tsx @@ -4,11 +4,21 @@ import FA from 'react-fontawesome'; import ToggleSwitch from '~/components/ToggleSwitch'; interface CustomQuestionProps { + + //the question this component describes question: Question; + + //callback for the delete question button onDelete: () => void; + + //callback for the questions' required switch toggle onChangeRequired: (newRequired: boolean) => void; } +/** + * This component renders a custom question on ./SettingsTab.tsx + * It handles the functionality for toggling the required field, and deleting the question. + */ class CustomQuestion extends React.Component { render() { diff --git a/src/client/pages/EventPage/tabs/SettingsTab.tsx b/src/client/pages/EventPage/tabs/SettingsTab.tsx index 0c185d69..cdcf4265 100644 --- a/src/client/pages/EventPage/tabs/SettingsTab.tsx +++ b/src/client/pages/EventPage/tabs/SettingsTab.tsx @@ -14,10 +14,19 @@ import EventPageTab from './EventPageTab'; interface SettingsTabProps { } +//TODO: not sure why this exists as opposed to a boolean? interface SettingsTabState { customQuestionsRequests: number; } +/** + * This is the settings tag for an event. This tab currently has: + * + * - Toggling event options + * - Add custom question + * - Update custom question + * - Delete custom question + */ export default class SettingsTab extends EventPageTab { state: Readonly = { customQuestionsRequests: 0, @@ -25,7 +34,7 @@ export default class SettingsTab extends EventPageTab { const { event, addEventSuccessAlert, addEventDangerAlert } = this.props; @@ -41,18 +50,31 @@ export default class SettingsTab extends EventPageTab { this.setState({ customQuestionsRequests: this.state.customQuestionsRequests + 1, }); }; + /** + * Set React state to hide loader after server response has been seen. + */ stopCustomQuestionsLoading = () => { this.setState({ customQuestionsRequests: this.state.customQuestionsRequests - 1, }); }; + /** + * Handles the CustomQuestions callback for when custom questions should be added. + * + * @param {QuestionType} type The type of question being added + * @param {Question} question The new question to send to the server. + */ onAddCustomQuestion = (type: QuestionType, question: Question) => { const { event, loadAllAdminEvents, addEventDangerAlert } = this.props; @@ -67,6 +89,13 @@ export default class SettingsTab extends EventPageTab { const { event, loadAllAdminEvents, addEventDangerAlert } = this.props; @@ -81,6 +110,12 @@ export default class SettingsTab extends EventPageTab { const { event, loadAllAdminEvents, addEventDangerAlert } = this.props; diff --git a/src/client/pages/EventPage/tabs/StatisticsTab.tsx b/src/client/pages/EventPage/tabs/StatisticsTab.tsx index 3a695c60..53077e09 100644 --- a/src/client/pages/EventPage/tabs/StatisticsTab.tsx +++ b/src/client/pages/EventPage/tabs/StatisticsTab.tsx @@ -12,6 +12,12 @@ interface StatisticsTabProps { statistics: EventStatistics | null; } +/** + * This is the tab that shows the user statistics for an event. This tab currently has: + * + * - Status Breakdown Piechart + * - Gender Breakdown Piechart + */ export default class StatisticsTab extends EventPageTab { render() { From 21232d23d72fba281cd3591c0ca1be38d23e7d8d Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 16:33:16 -0700 Subject: [PATCH 08/31] Event, HomePage Documentation complete --- .../EventPage/components/EventOptionsEdit.tsx | 22 ++++++++++++++ .../components/EventStatisticsCharts.tsx | 9 ++++++ .../components/EventStatisticsComponent.tsx | 3 ++ .../EventPage/components/GenderStatistics.tsx | 9 ++++++ .../EventPage/components/OrganiserList.tsx | 30 +++++++++++++++++++ .../EventPage/components/QuestionInput.tsx | 8 +++++ .../EventPage/components/ResumeStatistics.tsx | 3 ++ .../EventPage/components/SponsorList.tsx | 29 ++++++++++++++++++ .../HomePage/components/CurrentEvents.tsx | 3 ++ .../pages/HomePage/components/UserEvents.tsx | 5 ++++ src/client/pages/HomePage/index.tsx | 17 +++++++++++ 11 files changed, 138 insertions(+) diff --git a/src/client/pages/EventPage/components/EventOptionsEdit.tsx b/src/client/pages/EventPage/components/EventOptionsEdit.tsx index 627c0d9b..fa95ab4b 100644 --- a/src/client/pages/EventPage/components/EventOptionsEdit.tsx +++ b/src/client/pages/EventPage/components/EventOptionsEdit.tsx @@ -5,15 +5,24 @@ import { UncontrolledTooltip } from 'reactstrap'; import ToggleSwitch from '~/components/ToggleSwitch'; interface EventOptionsProps { + //initial options of the event options: TESCEventOptions; + + //calback function for when the update button is clicked onOptionsUpdate: (newOptions: TESCEventOptions) => void; + + //the event these options are for event: TESCEvent; } +//the current, edited state of the options interface EventOptionsState { options: TESCEventOptions; } +/** + * This component implements event option editing (toggling) + */ export default class EventOptionsEdit extends React.Component { constructor(props: EventOptionsProps) { super(props); @@ -23,6 +32,11 @@ export default class EventOptionsEdit extends React.Component () => { this.setState({ // TODO: Fix dynamically property @@ -31,6 +45,12 @@ export default class EventOptionsEdit extends React.Component ( @@ -44,6 +64,7 @@ export default class EventOptionsEdit extends React.ComponentUnique Universities, diff --git a/src/client/pages/EventPage/components/EventStatisticsComponent.tsx b/src/client/pages/EventPage/components/EventStatisticsComponent.tsx index f6f284dd..e2ece7b3 100644 --- a/src/client/pages/EventPage/components/EventStatisticsComponent.tsx +++ b/src/client/pages/EventPage/components/EventStatisticsComponent.tsx @@ -7,6 +7,9 @@ interface EventStatisticsComponentProps { statistics: EventStatistics | null; } +/** + * This is an abstraction that is used by statistics components to provide an event and statistics prop to them. + */ export default class EventStatisticsComponent

extends React.Component

{ diff --git a/src/client/pages/EventPage/components/GenderStatistics.tsx b/src/client/pages/EventPage/components/GenderStatistics.tsx index f04959a4..cec75d20 100644 --- a/src/client/pages/EventPage/components/GenderStatistics.tsx +++ b/src/client/pages/EventPage/components/GenderStatistics.tsx @@ -7,7 +7,16 @@ import EventStatisticsComponent from './EventStatisticsComponent'; const PIE_CHART_COLOURS = ['#8E44AD', '#43D2F0', '#AEF9D6', '#EF767A', '#7D7ABC']; +/** + * This component renders a statistics pie chart for the gender breakdown of an event + */ export default class GenderStatistics extends EventStatisticsComponent { + + /** + * Render the gender breakdown in text form + * + * @param {EventStatistics} statistics The statistics of the event + */ renderStats(statistics: EventStatistics) { return [

Gender Distribution
, diff --git a/src/client/pages/EventPage/components/OrganiserList.tsx b/src/client/pages/EventPage/components/OrganiserList.tsx index 9b7281b0..e4c56af8 100644 --- a/src/client/pages/EventPage/components/OrganiserList.tsx +++ b/src/client/pages/EventPage/components/OrganiserList.tsx @@ -7,27 +7,49 @@ import NewAdminModal, { NewAdminModalFormData } from '~/components/NewAdminModal import OrganiserSelect from '~/components/OrganiserSelect'; interface OrganiserListProps { + + //list of organisers for the event organisers: Admin[]; + + //callback function to show a modal to add a new organiser to the event addNewOrganiser: (toAdd: AdminSelectType) => void; + + //callback to add the organiser to the event registerNewOrganiser: (newOrganiser: NewAdminModalFormData) => void; } interface OrganiserListState { + + //the new organiser to be added to the event newOrganiser: AdminSelectType; + + //boolean to track if the new organiser modal is open or not isRegisterModalOpen: boolean; } +/** + * This component renders an organiser list on the administrators tab of an event + */ export default class OrganiserList extends React.Component { state: Readonly = { newOrganiser: null, isRegisterModalOpen: false, }; + + /** + * Update the components newOrganiser state to the new data + * + * @param {AdminSelectType} newOrganiser the new organiser to be set + */ changeNewOrganiser = (newOrganiser: AdminSelectType) => this.setState({ newOrganiser, }); + /** + * Add an (existing organiser) to the system from the dropdown + */ onAddNewOrganiser = () => { const {newOrganiser} = this.state; @@ -36,12 +58,20 @@ export default class OrganiserList extends React.Component { this.props.registerNewOrganiser(values); this.toggleRegisterModal(); }; + /** + * Toggle the react state to show the modal or not. + */ toggleRegisterModal = () => this.setState({ isRegisterModalOpen: !this.state.isRegisterModalOpen, }); diff --git a/src/client/pages/EventPage/components/QuestionInput.tsx b/src/client/pages/EventPage/components/QuestionInput.tsx index e43c2d5c..680988fe 100644 --- a/src/client/pages/EventPage/components/QuestionInput.tsx +++ b/src/client/pages/EventPage/components/QuestionInput.tsx @@ -12,14 +12,22 @@ interface QuestionInputState { isRequired: boolean; } +/** + * This component creates an input field to create a custom question. + */ export default class QuestionInput extends React.Component { state: Readonly = { question: '', isRequired: false, }; + /** + * Callback function called when the add button is pressed to add this question to the system + */ addQuestion = () => { this.props.onAddQuestion(this.state); + + //reset the component state this.setState({ question: '', isRequired: false, diff --git a/src/client/pages/EventPage/components/ResumeStatistics.tsx b/src/client/pages/EventPage/components/ResumeStatistics.tsx index f81d9480..f74c680b 100644 --- a/src/client/pages/EventPage/components/ResumeStatistics.tsx +++ b/src/client/pages/EventPage/components/ResumeStatistics.tsx @@ -9,6 +9,9 @@ interface ResumeStatisticsProps { className: string; } +/** + * Event header link to show the number of resumes in the system + */ export default class ResumeStatistics extends EventStatisticsComponent { /** * Renders the tooltip that explains how to approve resumes. diff --git a/src/client/pages/EventPage/components/SponsorList.tsx b/src/client/pages/EventPage/components/SponsorList.tsx index d7794b27..9e94556a 100644 --- a/src/client/pages/EventPage/components/SponsorList.tsx +++ b/src/client/pages/EventPage/components/SponsorList.tsx @@ -7,27 +7,48 @@ import NewAdminModal, { NewAdminModalFormData } from '~/components/NewAdminModal import SponsorSelect from '~/components/SponsorSelect'; interface SponsorListProps { + + //the sponsors for this event sponsors: Admin[]; + + //callback function to add an existing sponsor to the event addNewSponsor: (toAdd: AdminSelectType) => void; + + //function called to create a new sponsor in the system registerNewSponsor: (newSponsor: NewAdminModalFormData) => void; } interface SponsorListState { + + //the new sponsor to be added newSponsor: AdminSelectType; + + //boolean to track if the create sponsor modal is open or not. isRegisterModalOpen: boolean; } +/** + * This component renders a sponsor list on the administrators tab of an event + */ export default class SponsorList extends React.Component { state: Readonly = { newSponsor: null, isRegisterModalOpen: false, }; + /** + * Update the components newSponsor state to the new data + * + * @param {AdminSelectType} newOrganiser the new sponsor to be set + */ changeNewSponsor = (newSponsor: AdminSelectType) => this.setState({ newSponsor, }); + /** + * Add an (existing sponsor) to the system from the dropdown + */ onAddNewSponsor = () => { const {newSponsor} = this.state; @@ -36,12 +57,20 @@ export default class SponsorList extends React.Component { this.props.registerNewSponsor(values); this.toggleRegisterModal(); }; + /** + * Toggle the react state to show the modal or not. + */ toggleRegisterModal = () => this.setState({ isRegisterModalOpen: !this.state.isRegisterModalOpen, }); diff --git a/src/client/pages/HomePage/components/CurrentEvents.tsx b/src/client/pages/HomePage/components/CurrentEvents.tsx index 647232a7..66e1d109 100644 --- a/src/client/pages/HomePage/components/CurrentEvents.tsx +++ b/src/client/pages/HomePage/components/CurrentEvents.tsx @@ -6,6 +6,9 @@ interface CurrentEventProps { events: TESCEvent[]; } +/** + * This component shows all upcoming events with open applications + */ export default class CurrentEvents extends React.Component { render() { const { events } = this.props; diff --git a/src/client/pages/HomePage/components/UserEvents.tsx b/src/client/pages/HomePage/components/UserEvents.tsx index 5819f021..6c1f8e1a 100644 --- a/src/client/pages/HomePage/components/UserEvents.tsx +++ b/src/client/pages/HomePage/components/UserEvents.tsx @@ -3,9 +3,14 @@ import React from 'react'; import { Link } from 'react-router-dom'; interface UserEventsProps { + + //the events that the user has applied to events: TESCEvent[]; } +/** + * This component shows all existing applications for the user. + */ export default class UserEvents extends React.Component { render() { const { events } = this.props; diff --git a/src/client/pages/HomePage/index.tsx b/src/client/pages/HomePage/index.tsx index 5d2eece1..23642159 100644 --- a/src/client/pages/HomePage/index.tsx +++ b/src/client/pages/HomePage/index.tsx @@ -31,7 +31,12 @@ interface HomePageProps { type Props = ReturnType & ReturnType & HomePageProps; +/** + * This is the main tesc.event homepage. If the user requesting it is authenticated, it will show + * the user their existing applications. If the user is not authenticated, it will show all open applications + */ class HomePage extends React.Component { + componentDidMount() { this.props.showLoading(); @@ -53,6 +58,11 @@ class HomePage extends React.Component { return true; } + /** + * Renders the user's existing applications + * + * @param {TESCEvent[]} events the events that this user has applied to + */ userEvents(events: TESCEvent[]) { return (
@@ -61,6 +71,12 @@ class HomePage extends React.Component { ); } + /** + * Renders the current events that are open for applications + * + * @param {TESCEvent[]} events the events to be rendered + * @param {Boolean} small display mode for the event cards + */ currentEvents(events: TESCEvent[], small: boolean = false) { return (
@@ -72,6 +88,7 @@ class HomePage extends React.Component { render() { const { events, userEvents } = this.props; + //show the sidebar if the authenticated user has any applications const showSidebar = Object.values(userEvents).length > 0; let currentEvents: TESCEvent[] = []; From 4e151e47863cfdf12ac28caf41a31b377002eb0a Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 16:42:30 -0700 Subject: [PATCH 09/31] NewEventPage Documentation Complete --- src/client/components/EventForm.tsx | 14 ++++++++++++++ src/client/pages/NewEventPage/index.tsx | 10 ++++++++++ src/client/pages/NewEventPage/validate.ts | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/client/components/EventForm.tsx b/src/client/components/EventForm.tsx index 9ee7c86e..420820a3 100644 --- a/src/client/components/EventForm.tsx +++ b/src/client/components/EventForm.tsx @@ -21,10 +21,19 @@ interface EventFormProps { editing?: boolean; } +//the props of this component are the props returned by the redux-form HOC and it's native props type Props = InjectedFormProps & EventFormProps; +/** + * This is the redux-form to create a new event + */ class EventForm extends React.Component { + /** + * Create a file droppable field for the event logo + * + * @returns {Component} + */ createLogoUpload() { return ( { ); } + /** + * Show an alert that flags the event as a non-TESC hosted event. + * + * @returns {React.StatelessComponent} + */ showThirdPartyText: React.StatelessComponent = ({values}) => { if (values && values.organisedBy && values.organisedBy.input.value !== 'TESC') { return ( diff --git a/src/client/pages/NewEventPage/index.tsx b/src/client/pages/NewEventPage/index.tsx index 261496c6..da8ad79e 100644 --- a/src/client/pages/NewEventPage/index.tsx +++ b/src/client/pages/NewEventPage/index.tsx @@ -28,12 +28,22 @@ interface NewEventPageState { err: Error; } +/** + * This page holds the form to create a new event in the system + */ class NewEventPage extends React.Component { state: Readonly = { err: null, }; + /** + * Create a new event in the system + * + * @param {NewEventFormData} event the event to be created + */ createNewEvent = (event: EventFormData) => { + + //send event to API registerNewEvent(event) .then((res: TESCEvent) => { this.setState({ err: null }); diff --git a/src/client/pages/NewEventPage/validate.ts b/src/client/pages/NewEventPage/validate.ts index d6a025ce..667272db 100644 --- a/src/client/pages/NewEventPage/validate.ts +++ b/src/client/pages/NewEventPage/validate.ts @@ -1,5 +1,7 @@ +/** + * New Event Form validator (Redux Form) + */ const createValidator = (requireLogo = true, allowPastDates = false) => (values: any) => { - console.log(values) const errors: any = {}; const required = ['name', 'alias', 'closeTimeMonth', 'closeTimeDay', From adc8bb3dbc1fa0b4e56494d3774125c744e87402 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 17:02:53 -0700 Subject: [PATCH 10/31] TODO RSVPConfirm onwards and UsersPage --- .../ResumesPage/components/ResumeList.tsx | 11 +++++++++++ src/client/pages/ResumesPage/index.tsx | 18 +++++++++++++++--- .../pages/UserPage/components/BussingModal.tsx | 15 +++++++++++++++ .../pages/UserPage/components/RSVPConfirm.tsx | 3 +++ src/client/pages/UserPage/index.tsx | 10 +++++++++- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/client/pages/ResumesPage/components/ResumeList.tsx b/src/client/pages/ResumesPage/components/ResumeList.tsx index dad98060..0bd57c09 100644 --- a/src/client/pages/ResumesPage/components/ResumeList.tsx +++ b/src/client/pages/ResumesPage/components/ResumeList.tsx @@ -3,8 +3,14 @@ import React from 'react'; import ToggleSwitch from '~/components/ToggleSwitch'; interface ResumeListProps { + + //callback for when the compact state toggle is clicked onCompactChange: () => void; + + //the compacted state isCompacted: boolean; + + //the users to render applicants: TESCUser[]; } @@ -13,11 +19,16 @@ interface ColumnMap { } interface ResumeListState { + + //columns shown on screen columns: ColumnMap; smallColumns: string[]; mediumColumns: string[]; } +/** + * This is the resume list that is rendered in the sponsor tool + */ class ResumeList extends React.Component { state: Readonly = { columns: { diff --git a/src/client/pages/ResumesPage/index.tsx b/src/client/pages/ResumesPage/index.tsx index b14e2c0e..dcb77739 100644 --- a/src/client/pages/ResumesPage/index.tsx +++ b/src/client/pages/ResumesPage/index.tsx @@ -1,4 +1,4 @@ -import { TESCUser } from '@Shared/ModelTypes'; + import { TESCUser } from '@Shared/ModelTypes'; import { UserStatus } from '@Shared/UserStatus'; import React from 'react'; import { connect } from 'react-redux'; @@ -38,18 +38,30 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface ResumesPageProps { } -type Props = RouteProps & ReturnType & ReturnType & ResumesPageProps; +// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// regular props of the component +type Props = RouteComponentProps<{ + eventAlias: string; +}> & ReturnType & ReturnType & ResumesPageProps; interface ResumesPageState { + + //boolean to track compact state isCompacted: boolean; } +/** + * This is the sponsor tool that shows a list of applicants to an event and their resumes + */ class ResumesPage extends React.Component { state: Readonly = { isCompacted: false, }; - toggleCompacted = () => this.setState({ isCompacted: !this.state.isCompacted }); + /** + * Toggle to compact react state + */ + toggleCompacted = () => this.setState({isCompacted: !this.state.isCompacted}); componentDidMount() { const { showLoading, hideLoading } = this.props; diff --git a/src/client/pages/UserPage/components/BussingModal.tsx b/src/client/pages/UserPage/components/BussingModal.tsx index 19eabf63..53fdd2c2 100644 --- a/src/client/pages/UserPage/components/BussingModal.tsx +++ b/src/client/pages/UserPage/components/BussingModal.tsx @@ -2,12 +2,27 @@ import React from 'react'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface BussingModalProps { + + //boolean to track if the modal is open isOpen: boolean; + + //is a bus available for this school? availableBus?: string; + + //callback for when the bus is clicked onChooseBus: (choice: boolean) => void; } +/** + * This is the modal that a user can use to show that they will be getting a bus. + */ export default class BussingModal extends React.Component { + + /** + * Callback for the bus choose button + * + * @param {Boolean} bussing boolean to track bussing status + */ onChooseBus = (bussing: boolean) => () => this.props.onChooseBus(bussing); diff --git a/src/client/pages/UserPage/components/RSVPConfirm.tsx b/src/client/pages/UserPage/components/RSVPConfirm.tsx index d0c5a0e3..8dfdfd38 100644 --- a/src/client/pages/UserPage/components/RSVPConfirm.tsx +++ b/src/client/pages/UserPage/components/RSVPConfirm.tsx @@ -16,6 +16,9 @@ interface RSVPConfirmState { status: boolean; } +/** + * + */ export default class RSVPConfirm extends React.Component { state: Readonly = { page: 0, diff --git a/src/client/pages/UserPage/index.tsx b/src/client/pages/UserPage/index.tsx index 69fa0be5..bd5e2129 100644 --- a/src/client/pages/UserPage/index.tsx +++ b/src/client/pages/UserPage/index.tsx @@ -33,6 +33,8 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface UserPageProps { } +// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// regular props of the component type Props = RouteComponentProps<{ eventAlias: string; }> & ReturnType & ReturnType & UserPageProps; @@ -41,6 +43,9 @@ interface UserPageState extends AlertPageState { showRSVP: boolean; } +/** + * This is the page that shows a user their application + */ class UserPage extends AlertPage { state: Readonly = { alerts: [], @@ -91,7 +96,10 @@ class UserPage extends AlertPage { }); } - toggleRSVP = () => this.setState({ showRSVP: !this.state.showRSVP }); + /** + * Toggle React state to show the RSVP modal + */ + toggleRSVP = () => this.setState({showRSVP: !this.state.showRSVP}); /** * Requests that the server RSVP the current user with the given values. From 8bc1755d1dc25dd0d692d812c96fa9071bbbe89a Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 17:32:32 -0700 Subject: [PATCH 11/31] User and UsersPage done --- .../pages/UserPage/components/RSVPConfirm.tsx | 8 +++- .../pages/UserPage/components/RSVPModal.tsx | 13 ++++++ .../pages/UserPage/components/UserProfile.tsx | 46 ++++++++++++++++++- .../UsersPage/components/ColumnEditor.tsx | 2 +- .../pages/UsersPage/components/UserList.tsx | 12 +++++ src/client/pages/UsersPage/index.tsx | 3 ++ 6 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/client/pages/UserPage/components/RSVPConfirm.tsx b/src/client/pages/UserPage/components/RSVPConfirm.tsx index 8dfdfd38..ba9057ca 100644 --- a/src/client/pages/UserPage/components/RSVPConfirm.tsx +++ b/src/client/pages/UserPage/components/RSVPConfirm.tsx @@ -17,7 +17,7 @@ interface RSVPConfirmState { } /** - * + * This is the component that renders the RSVP workflow by showing RSVPModal and BussingModal */ export default class RSVPConfirm extends React.Component { state: Readonly = { @@ -25,6 +25,7 @@ export default class RSVPConfirm extends React.Component this.setState({page: this.state.page + 1}); onChooseStatus = (status: boolean) => { @@ -45,6 +46,11 @@ export default class RSVPConfirm extends React.Component { const {onUpdate, onClose} = this.props; diff --git a/src/client/pages/UserPage/components/RSVPModal.tsx b/src/client/pages/UserPage/components/RSVPModal.tsx index c1e48a21..602f53a7 100644 --- a/src/client/pages/UserPage/components/RSVPModal.tsx +++ b/src/client/pages/UserPage/components/RSVPModal.tsx @@ -3,14 +3,27 @@ import React from 'react'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface RSVPModalProps { + + //toggle function for the model toggle: () => void; + + //variable to track open or close state of the modal isOpen: boolean; + //callback function for when the status is chosen onChooseStatus: (statusChoice: boolean) => void; + + //the event for which the current application is on event: TESCEvent; } export default class RSVPModal extends React.Component { + + /** + * Callback for when an RSVP status is chosen + * + * @param {Boolean} status RSVP true or false + */ onChooseStatus = (status: boolean) => () => this.props.onChooseStatus(status); render() { diff --git a/src/client/pages/UserPage/components/UserProfile.tsx b/src/client/pages/UserPage/components/UserProfile.tsx index bde8da5f..c7109488 100644 --- a/src/client/pages/UserPage/components/UserProfile.tsx +++ b/src/client/pages/UserPage/components/UserProfile.tsx @@ -7,6 +7,7 @@ import { CustomFieldProps } from '~/components/Fields'; import FileField from '~/components/FileField'; import { generateQRCodeURL } from '@Shared/QRCodes'; import FA from 'react-fontawesome'; +import { JSXElement } from 'babel-types'; export interface UserProfileFormData { gender: string; @@ -23,14 +24,31 @@ export interface UserProfileFormData { } interface UserProfileProps { + + //the user for which the profile is rendered user: TESCUser; + + //the event for which the application is on event: TESCEvent; + + //callback function to toggle RSVP status toggleRSVP: () => void; } type Props = InjectedFormProps & UserProfileProps; +/** + * This is the component that shows the user their data on the application page + * + * It also provides functionality to edit their application + */ class UserProfile extends React.Component { + /** + * Render the gender selection. + * + * @param {Object} _ an object with an input and className field + * @returns {React.StatelessComponent} + */ genderSelect: React.StatelessComponent = ({ input, className }) => { return ( ); }; - + + /** + * Render the T-Shirt size selection. + * + * @param {Object} _ an object with an input and className field + * @returns {React.StatelessComponent} + */ pronounSelect: React.StatelessComponent = ({ input, className }) => { return ( ); }; - + /** * Render the T-Shirt size selection. - * + * * @param {Object} _ an object with an input and className field * @returns {React.StatelessComponent} */ @@ -188,9 +188,9 @@ class UserProfile extends React.Component { /** * Render the phone number and santitize it - * + * * @param {String} phone the phone number string - * @returns {String} + * @returns {String} */ renderPhoneNumber = (phone: string) => ( phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3') @@ -198,7 +198,7 @@ class UserProfile extends React.Component { /** * Render the Applicant's info - * + * * @param {TESCUser} user the info of the applicant * @returns {JSXElement} */ @@ -267,7 +267,7 @@ class UserProfile extends React.Component { /** * Render a user's preference section. - * + * * @param {TESCUser} user the user's data * @returns {JSXElement} */ diff --git a/src/client/pages/UserPage/index.tsx b/src/client/pages/UserPage/index.tsx index bd5e2129..c215f767 100644 --- a/src/client/pages/UserPage/index.tsx +++ b/src/client/pages/UserPage/index.tsx @@ -33,7 +33,7 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface UserPageProps { } -// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// the props of this event are the union of the react-router data, redux actions and dispatch, and the // regular props of the component type Props = RouteComponentProps<{ eventAlias: string; diff --git a/src/client/pages/UsersPage/components/ColumnEditor.tsx b/src/client/pages/UsersPage/components/ColumnEditor.tsx index 71260659..5ed97aa2 100644 --- a/src/client/pages/UsersPage/components/ColumnEditor.tsx +++ b/src/client/pages/UsersPage/components/ColumnEditor.tsx @@ -83,4 +83,4 @@ export default class UserList extends React.Component ); } -} \ No newline at end of file +} diff --git a/src/client/pages/UsersPage/components/UserList.tsx b/src/client/pages/UsersPage/components/UserList.tsx index 13e147bb..28d78fae 100644 --- a/src/client/pages/UsersPage/components/UserList.tsx +++ b/src/client/pages/UsersPage/components/UserList.tsx @@ -11,7 +11,7 @@ const styles = require('react-table/react-table.css'); interface UserListProps { - //users that have applied to an event + // users that have applied to an event users: TESCUser[]; columns: AutofillColumn[]; event: TESCEvent; @@ -26,7 +26,7 @@ class UserList extends React.Component { /** * Render the User component, used by react-table's SubComponent - * + * * @param {TESCUser} row the user to be rendered * @returns {JSXElement} */ @@ -49,7 +49,7 @@ class UserList extends React.Component { data={this.props.users} column={{ ...ReactTableDefaults.column, - Cell: ({value}) => value ? String(value): value + Cell: ({value}) => value ? String(value): value, }} columns={this.props.columns} defaultPageSize={10} diff --git a/src/client/pages/UsersPage/index.tsx b/src/client/pages/UsersPage/index.tsx index e6612f3a..69902e50 100644 --- a/src/client/pages/UsersPage/index.tsx +++ b/src/client/pages/UsersPage/index.tsx @@ -44,7 +44,7 @@ type RouteProps = RouteComponentProps<{ eventAlias: string; }>; -//the props of this page is the union of the react-router, redux and explicit props +// the props of this page is the union of the react-router, redux and explicit props type Props = RouteProps & ReturnType & ReturnType & UsersPageProps; interface UsersPageState extends AlertPageState { @@ -132,7 +132,7 @@ class UsersPage extends AlertPage { /** * Handles an update to a user in the list. - * + * * @param {TESCUser} user the user to be updated */ onUserUpdate = (user: TESCUser) => { diff --git a/src/client/routes.tsx b/src/client/routes.tsx index 91ff0028..2f197391 100644 --- a/src/client/routes.tsx +++ b/src/client/routes.tsx @@ -13,34 +13,24 @@ import { authorised as AdminAuthorised } from '~/data/AdminApi'; import { authorised as UserAuthorised } from '~/data/UserApi'; import CookieTypes from '~/static/Cookies'; -/* - PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around - react-router-dom's Route component to handle authentication state. -*/ import PrivateRoute from './PrivateRoute'; import PrivateUserRoute from './PrivateUserRoute'; - -//Authentication Components & Actions -//TODO: Document better import { ApplicationDispatch } from './actions'; import AdminLogout from './auth/admin/Logout'; import { finishAuthorisation, authoriseAdmin, logoutAdmin } from './auth/admin/actions'; import ConfirmPage from './auth/user/Confirm'; import UserLogout from './auth/user/Logout'; import { authoriseUser, finishAuthorisation as finishUserAuth, logoutUser } from './auth/user/actions'; - -//Importing the different layouts (page structures) for the application import AdminLayout from './layouts/admin'; -import SponsorLayout from './layouts/sponsor'; import PublicLayout from './layouts/public'; +import SponsorLayout from './layouts/sponsor'; import UserLayout from './layouts/user'; - -//Importing all the pages for the app, used later when setting up routes. import AdminsPage from './pages/AdminsPage'; import ApplyPage from './pages/ApplyPage'; import CheckinPage from './pages/CheckinPage'; import Dashboard from './pages/DashboardPage'; import EventPage from './pages/EventPage'; +import PreviewApplication from './pages/EventPage/components/PreviewApplication'; import ForgotPage from './pages/ForgotPage'; import HomePage from './pages/HomePage'; import LoginPage from './pages/LoginPage'; @@ -50,7 +40,18 @@ import ResetPage from './pages/ResetPage'; import ResumesPage from './pages/ResumesPage'; import UserPage from './pages/UserPage'; import UsersPage from './pages/UsersPage'; -import PreviewApplication from './pages/EventPage/components/PreviewApplication'; + +/* + PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around + react-router-dom's Route component to handle authentication state. +*/ + +// Authentication Components & Actions +// TODO: Document better + +// Importing the different layouts (page structures) for the application + +// Importing all the pages for the app, used later when setting up routes. const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators({ authoriseAdmin, @@ -167,7 +168,7 @@ class Routes extends React.Component { } renderPublic = (RenderComponent: any) => { - return (props: RouteComponentProps) => + return (props: RouteComponentProps) => ( diff --git a/src/shared/QRCodes.ts b/src/shared/QRCodes.ts index 74e80741..40f62f53 100644 --- a/src/shared/QRCodes.ts +++ b/src/shared/QRCodes.ts @@ -1,10 +1,10 @@ import { TESCUser } from '@Shared/ModelTypes'; const QR_CODE_SIZE = 200; -const QR_CODE_API_ROOT = `https://api.qrserver.com/v1/create-qr-code/` +const QR_CODE_API_ROOT = `https://api.qrserver.com/v1/create-qr-code/`; function generateQRCodeURL(user: TESCUser) { - return `${QR_CODE_API_ROOT}?size=${QR_CODE_SIZE}x${QR_CODE_SIZE}&data=${user._id}` + return `${QR_CODE_API_ROOT}?size=${QR_CODE_SIZE}x${QR_CODE_SIZE}&data=${user._id}`; } -export { generateQRCodeURL } \ No newline at end of file +export { generateQRCodeURL }; diff --git a/src/shared/api/Requests.ts b/src/shared/api/Requests.ts index 060774e7..4ba587ef 100644 --- a/src/shared/api/Requests.ts +++ b/src/shared/api/Requests.ts @@ -142,4 +142,4 @@ export interface RSVPUserRequest { export interface StatusEmailRequest { user: TESCUser; -} \ No newline at end of file +} diff --git a/src/shared/api/Responses.ts b/src/shared/api/Responses.ts index dd897ca9..2bdac334 100644 --- a/src/shared/api/Responses.ts +++ b/src/shared/api/Responses.ts @@ -14,7 +14,7 @@ export interface EventStatistics { resumes: number; appsOverTime: { [Date: string]: number; - } + }; } export type EventUserCounts = { From 9af8651a81bfdbf67bcf98b333c23ae718ac1498 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 8 Aug 2020 16:30:03 -0400 Subject: [PATCH 14/31] Ran tslint --fix --- src/client/components/EventForm.tsx | 6 +-- src/client/components/Fields.tsx | 22 +++++---- src/client/components/Hero.tsx | 4 +- src/client/components/User.tsx | 6 +-- src/client/data/AdminApi.ts | 17 ++++--- src/client/layouts/public.tsx | 2 +- src/client/layouts/sponsor.tsx | 4 +- src/client/main.tsx | 2 +- .../pages/AdminsPage/components/AdminList.tsx | 20 ++++---- src/client/pages/AdminsPage/index.tsx | 8 ++-- .../pages/AdminsPage/reducers/Admins.ts | 2 +- .../ApplyPage/components/ApplyPageSection.tsx | 4 +- .../pages/ApplyPage/components/Header.tsx | 6 +-- .../ApplyPage/components/PersonalSection.tsx | 6 +-- .../ApplyPage/components/ResponseSection.tsx | 2 +- .../ApplyPage/components/UniversityField.tsx | 2 +- src/client/pages/ApplyPage/index.tsx | 22 ++++----- .../components/AdminDashboard.tsx | 4 +- .../DashboardPage/components/EventList.tsx | 8 ++-- .../components/SponsorDashboard.tsx | 4 +- src/client/pages/DashboardPage/index.tsx | 8 ++-- .../components/AppsOverTimeStatistics.tsx | 6 +-- .../pages/EventPage/components/BulkChange.tsx | 6 +-- .../EventPage/components/CustomQuestion.tsx | 8 ++-- .../EventPage/components/EventOptionsEdit.tsx | 18 +++---- .../components/EventStatisticsCharts.tsx | 4 +- .../EventPage/components/GenderStatistics.tsx | 4 +- .../EventPage/components/OrganiserList.tsx | 15 +++--- .../components/PreviewApplication.tsx | 5 +- .../EventPage/components/QuestionInput.tsx | 2 +- .../EventPage/components/SponsorList.tsx | 16 +++---- .../EventPage/components/ViewApplication.tsx | 12 ++--- src/client/pages/EventPage/index.tsx | 20 ++++---- .../pages/EventPage/tabs/ActionsTab.tsx | 14 +++--- .../EventPage/tabs/AdministratorsTab.tsx | 6 +-- .../pages/EventPage/tabs/SettingsTab.tsx | 13 +++-- .../pages/EventPage/tabs/StatisticsTab.tsx | 4 +- src/client/pages/ForgotPage.tsx | 2 +- .../pages/HomePage/components/UserEvents.tsx | 2 +- src/client/pages/HomePage/index.tsx | 8 ++-- src/client/pages/NewEventPage/index.tsx | 6 +-- src/client/pages/NewEventPage/validate.ts | 2 +- .../ResumesPage/components/ResumeList.tsx | 8 ++-- src/client/pages/ResumesPage/index.tsx | 48 +++++++++---------- .../UserPage/components/BussingModal.tsx | 8 ++-- .../pages/UserPage/components/RSVPConfirm.tsx | 4 +- .../pages/UserPage/components/RSVPModal.tsx | 10 ++-- .../pages/UserPage/components/UserProfile.tsx | 32 ++++++------- src/client/pages/UserPage/index.tsx | 2 +- .../UsersPage/components/ColumnEditor.tsx | 2 +- .../pages/UsersPage/components/UserList.tsx | 6 +-- src/client/pages/UsersPage/index.tsx | 4 +- src/client/routes.tsx | 29 +++++------ src/shared/QRCodes.ts | 6 +-- src/shared/api/Requests.ts | 2 +- src/shared/api/Responses.ts | 2 +- tslint.json | 2 +- 57 files changed, 250 insertions(+), 247 deletions(-) diff --git a/src/client/components/EventForm.tsx b/src/client/components/EventForm.tsx index 420820a3..81b840a5 100644 --- a/src/client/components/EventForm.tsx +++ b/src/client/components/EventForm.tsx @@ -21,7 +21,7 @@ interface EventFormProps { editing?: boolean; } -//the props of this component are the props returned by the redux-form HOC and it's native props +// The props of this component are the props returned by the redux-form HOC and it's native props type Props = InjectedFormProps & EventFormProps; /** @@ -31,7 +31,7 @@ class EventForm extends React.Component { /** * Create a file droppable field for the event logo - * + * * @returns {Component} */ createLogoUpload() { @@ -47,7 +47,7 @@ class EventForm extends React.Component { /** * Show an alert that flags the event as a non-TESC hosted event. - * + * * @returns {React.StatelessComponent} */ showThirdPartyText: React.StatelessComponent = ({values}) => { diff --git a/src/client/components/Fields.tsx b/src/client/components/Fields.tsx index 83b90f0f..07bddf5c 100644 --- a/src/client/components/Fields.tsx +++ b/src/client/components/Fields.tsx @@ -1,4 +1,8 @@ -import { UserDiversityOptions, UserYearOptions, UserGenderOptions, UserPronounOptions, UserShirtSizeOptions } from '@Shared/UserEnums'; +import { UserDiversityOptions, + UserYearOptions, + UserGenderOptions, + UserPronounOptions, + UserShirtSizeOptions } from '@Shared/UserEnums'; import React from 'react'; import { Field, WrappedFieldProps } from 'redux-form'; import majors from '~/static/Majors.json'; @@ -48,7 +52,7 @@ export function errorClass(className: string, touched: boolean, error: boolean) } export const errorTextInput: React.StatelessComponent = ({ input, className, placeholder, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return (
@@ -81,7 +85,7 @@ export const errorRadio: React.StatelessComponent = ({ input, }; export const errorTextArea: React.StatelessComponent = ({ input, className, placeholder, maxLength, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return (
@@ -122,7 +126,7 @@ export const errorMonthPicker: React.StatelessComponent = ({ i }; export const errorTShirtSizePicker: React.StatelessComponent = ({ input, className, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); const sizes = Object.values(UserShirtSizeOptions); const values = Object.keys(UserShirtSizeOptions); @@ -143,7 +147,7 @@ export const errorTShirtSizePicker: React.StatelessComponent = }; export const errorGenderPicker: React.StatelessComponent = ({ input, className, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return ( @@ -162,7 +166,7 @@ export const errorGenderPicker: React.StatelessComponent = ({ }; export const errorPronounPicker: React.StatelessComponent = ({ input, className, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return ( @@ -181,7 +185,7 @@ export const errorPronounPicker: React.StatelessComponent = ({ }; export const errorDiversityOptions: React.StatelessComponent = ({ input, className, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return ( @@ -200,7 +204,7 @@ export const errorDiversityOptions: React.StatelessComponent = }; export const errorYearPicker: React.StatelessComponent = ({ input, className, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return ( @@ -219,7 +223,7 @@ export const errorYearPicker: React.StatelessComponent = ({ in }; export const errorMajorPicker: React.StatelessComponent = ({ input, className, type, - meta: { touched, error } }) => { + meta: { touched, error } }) => { const errorClassName = errorClass(className, touched, error); return ( diff --git a/src/client/components/Hero.tsx b/src/client/components/Hero.tsx index 4b9d6864..099fe355 100644 --- a/src/client/components/Hero.tsx +++ b/src/client/components/Hero.tsx @@ -22,8 +22,8 @@ export default class Hero extends React.Component { } return ( -
-
+ +
); } } diff --git a/src/client/components/User.tsx b/src/client/components/User.tsx index c397de05..9db922b7 100644 --- a/src/client/components/User.tsx +++ b/src/client/components/User.tsx @@ -1,4 +1,5 @@ import { TESCUser, TESCEvent, Question } from '@Shared/ModelTypes'; +import { generateQRCodeURL } from '@Shared/QRCodes'; import { QuestionType } from '@Shared/Questions'; import { getRoleRank, Role } from '@Shared/Roles'; import { isAcceptableStatus, isRejectableStatus, isWaitlistableStatus, UserStatus } from '@Shared/UserStatus'; @@ -9,7 +10,6 @@ import { connect } from 'react-redux'; import { Field, reduxForm, InjectedFormProps } from 'redux-form'; import { sendAcceptanceEmail, sendRejectionEmail, sendWaitlistEmail } from '~/data/AdminApi'; import { ApplicationState } from '~/reducers'; -import { generateQRCodeURL } from '@Shared/QRCodes' import { AlertType } from '../pages/AlertPage'; @@ -114,7 +114,7 @@ class User extends React.Component { className="form-control" component="select" type={fieldType}> - + )}
@@ -343,7 +343,7 @@ class User extends React.Component { {this.renderFormCheckbox('Bussing', 'bussing', 'col-sm-4')} {this.renderFormCheckbox('Sanitized', 'sanitized', 'col-sm-4')} - {this.renderFormDropdown('Status', 'status', + {this.renderFormDropdown('Status', 'status', Object.values(UserStatus), 'col-sm-4')}
diff --git a/src/client/data/AdminApi.ts b/src/client/data/AdminApi.ts index a2e31cec..802b5a98 100644 --- a/src/client/data/AdminApi.ts +++ b/src/client/data/AdminApi.ts @@ -23,8 +23,8 @@ import request, { SuperAgentRequest } from 'superagent'; import nocache from 'superagent-no-cache'; import pref from 'superagent-prefix'; import Cookies from 'universal-cookie'; -import { NewAdminModalFormData } from '~/components/NewAdminModal'; import { EventFormData } from '~/components/EventForm'; +import { NewAdminModalFormData } from '~/components/NewAdminModal'; import CookieTypes from '~/static/Cookies'; import { promisify } from './helpers'; @@ -195,7 +195,6 @@ export const editExistingEvent = (id: string, event: Partial) => closeTimeDay )).toISOString(true); - const promiseReq = request .post(`/events/edit/${id}`) .set('Authorization', cookies.get(CookieTypes.admin.token)) @@ -204,14 +203,14 @@ export const editExistingEvent = (id: string, event: Partial) => closeTime, } as RegisterEventRequest)) .use(adminApiPrefix) - .use(nocache) + .use(nocache); - if (logo) { - promiseReq.attach('logo', logo[0]) + if (logo) { + promiseReq.attach('logo', logo[0]); } - return promisify(promiseReq); -} + return promisify(promiseReq); +}; /** * Request to register a new admin. @@ -250,7 +249,7 @@ export const downloadResumes = (applicants: string[]): SuperAgentRequest => .post('/resumes') .send({ applicants } as DownloadResumesRequest) .set('Authorization', cookies.get(CookieTypes.admin.token)) - .use(adminApiPrefix) + .use(adminApiPrefix); /** * Requests the status of an ongoing download. @@ -450,4 +449,4 @@ export const sendWaitlistEmail = (user: TESCUser) => .send({ user } as StatusEmailRequest) .use(adminApiPrefix) .use(nocache) - ); \ No newline at end of file + ); diff --git a/src/client/layouts/public.tsx b/src/client/layouts/public.tsx index f399e131..f75a2319 100644 --- a/src/client/layouts/public.tsx +++ b/src/client/layouts/public.tsx @@ -12,4 +12,4 @@ class PublicLayout extends React.Component { } } -export default PublicLayout; \ No newline at end of file +export default PublicLayout; diff --git a/src/client/layouts/sponsor.tsx b/src/client/layouts/sponsor.tsx index 85d51ca5..7d796eb3 100644 --- a/src/client/layouts/sponsor.tsx +++ b/src/client/layouts/sponsor.tsx @@ -90,7 +90,7 @@ class SponsorLayout extends React.Component { const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); - + link.href = url; link.setAttribute('download', `${this.props.user.username}-${Date.now()}.csv`); document.body.appendChild(link); @@ -98,7 +98,7 @@ class SponsorLayout extends React.Component { hideLoading(); this.setState({ - isDownloading: false + isDownloading: false, }); }) .catch(console.error); diff --git a/src/client/main.tsx b/src/client/main.tsx index 3b0776ca..a110335e 100644 --- a/src/client/main.tsx +++ b/src/client/main.tsx @@ -22,7 +22,7 @@ const store = createStore(reducer, applyMiddleware(reduxThunk as ThunkMiddleware) )); -//Configuring the application for routing, cookies, and redux. +// Configuring the application for routing, cookies, and redux. render( ( diff --git a/src/client/pages/AdminsPage/components/AdminList.tsx b/src/client/pages/AdminsPage/components/AdminList.tsx index ff6d54cd..6734fe3d 100644 --- a/src/client/pages/AdminsPage/components/AdminList.tsx +++ b/src/client/pages/AdminsPage/components/AdminList.tsx @@ -5,13 +5,13 @@ import { Button } from 'reactstrap'; interface AdminListProps { - //the admins in the system + // the admins in the system admins: Admin[]; - //function to be called when the delete button is pressed + // function to be called when the delete button is pressed onDeleteAdmin: (adminId: string) => void; - //is the admin in editing mode? + // is the admin in editing mode? // TODO: remove - legacy feature editing: boolean; } @@ -34,7 +34,7 @@ export default class AdminList extends React.Component { const {columns} = this.state; - const adminToBeRendered = {...admin, - lastAccessed: admin.lastAccessed - ? moment(admin.lastAccessed).fromNow() - : 'Never Logged In' - } + const adminToBeRendered = {...admin, + lastAccessed: admin.lastAccessed + ? moment(admin.lastAccessed).fromNow() + : 'Never Logged In', + }; return Object.keys(columns).map(column => ( {/* @@ -68,7 +68,7 @@ export default class AdminList extends React.Component) - ) + ); } render() { diff --git a/src/client/pages/AdminsPage/index.tsx b/src/client/pages/AdminsPage/index.tsx index afcd9319..cf2281c0 100644 --- a/src/client/pages/AdminsPage/index.tsx +++ b/src/client/pages/AdminsPage/index.tsx @@ -25,11 +25,11 @@ interface AdminsPageProps { } /** - * This component receives props in 3 ways - + * This component receives props in 3 ways - * 1) The explicit props provied to it by AdminsPageProps * 2) The redux state provided to it by mapStateToProps * 3) The dispatch functions provided to it by mapDispatchToProps - * + * * So, the props of this component is the union of the return types of mapStateToProps, * mapDispatchToProps and AdminsPageProps */ @@ -37,7 +37,7 @@ type Props = ReturnType & ReturnType { componentDidMount() { this.props.showLoading(); - //Hide loading state after the API returns the admins + // Hide loading state after the API returns the admins this.loadAdmins() .then(() => this.props.hideLoading()); } diff --git a/src/client/pages/AdminsPage/reducers/Admins.ts b/src/client/pages/AdminsPage/reducers/Admins.ts index 6cf69dcd..f8f7c22c 100644 --- a/src/client/pages/AdminsPage/reducers/Admins.ts +++ b/src/client/pages/AdminsPage/reducers/Admins.ts @@ -7,7 +7,7 @@ import * as Types from '../actions/types'; const initialState: Admin[] = []; -//Tell redux what to do when it sees ADD_ADMINS and REPLACE_ADMINS +// Tell redux what to do when it sees ADD_ADMINS and REPLACE_ADMINS export default handleActions({ [Types.ADD_ADMINS]: (state, action: ActionType) => ([ ...state, diff --git a/src/client/pages/ApplyPage/components/ApplyPageSection.tsx b/src/client/pages/ApplyPage/components/ApplyPageSection.tsx index d5c15a4c..5fd47204 100644 --- a/src/client/pages/ApplyPage/components/ApplyPageSection.tsx +++ b/src/client/pages/ApplyPage/components/ApplyPageSection.tsx @@ -4,10 +4,10 @@ import { InjectedFormProps } from 'redux-form'; export interface ApplyPageSectionProps { - //The event this application is for + // The event this application is for event: TESCEvent; - //function to be called when the back button is pressed + // function to be called when the back button is pressed goToPreviousPage?: () => void; } diff --git a/src/client/pages/ApplyPage/components/Header.tsx b/src/client/pages/ApplyPage/components/Header.tsx index 9c068656..1f7192c4 100644 --- a/src/client/pages/ApplyPage/components/Header.tsx +++ b/src/client/pages/ApplyPage/components/Header.tsx @@ -3,13 +3,13 @@ import React from 'react'; interface HeaderProps { - //Name of the event + // Name of the event name: string; - //Logo of the event + // Logo of the event logo: Logo; - //Description of the event + // Description of the event description: string; } diff --git a/src/client/pages/ApplyPage/components/PersonalSection.tsx b/src/client/pages/ApplyPage/components/PersonalSection.tsx index 4d7a7b39..93c3f718 100644 --- a/src/client/pages/ApplyPage/components/PersonalSection.tsx +++ b/src/client/pages/ApplyPage/components/PersonalSection.tsx @@ -10,10 +10,10 @@ import UniversityField from './UniversityField'; interface PersonalSectionProps extends ApplyPageSectionProps { - //function to be called when the email changes, to check if the user exists in the database + // function to be called when the email changes, to check if the user exists in the database onEmailChange: (newEmail: string) => void; - //the current event being applied to + // the current event being applied to event: TESCEvent; } @@ -42,7 +42,7 @@ class PersonalSection extends ApplyPageSection { - + /** * Function that is called when the user selects a suggestion from the AutoSuggest * TODO: better documentation diff --git a/src/client/pages/ApplyPage/index.tsx b/src/client/pages/ApplyPage/index.tsx index e60ff3f9..e3a8e51f 100644 --- a/src/client/pages/ApplyPage/index.tsx +++ b/src/client/pages/ApplyPage/index.tsx @@ -19,29 +19,29 @@ interface ApplyPageProps { } type Props = ApplyPageProps & RouteComponentProps<{ - //eventAlias for the event this application is for + // eventAlias for the event this application is for eventAlias: string; }>; interface ApplyPageState { - //tracks which application page the user is on + // tracks which application page the user is on page: number; - //tracks application error + // tracks application error error: Error; - //tracks if the user has yet to submit the application + // tracks if the user has yet to submit the application isSubmitting: boolean; - //the event this application is for + // the event this application is for event: TESCEvent; - //does the user have an exisiting tesc.events account? + // does the user have an exisiting tesc.events account? emailExists: boolean; } -//The complete application is the union of data from its 3 pages +// The complete application is the union of data from its 3 pages export type ApplyPageFormData = PersonalSectionFormData & ResponseSectionFormData & UserSectionFormData; /** @@ -109,7 +109,7 @@ class ApplyPage extends React.Component { */ sanitiseValues(values: ApplyPageFormData) { - //parse date numbers into a datestring + // parse date numbers into a datestring values.birthdate = new Date( values.birthdateYear, values.birthdateMonth - 1, @@ -151,7 +151,7 @@ class ApplyPage extends React.Component { return; } - //checks if user exists with an API call + // checks if user exists with an API call checkUserExists(email) .then((ret) => { this.setState({ @@ -174,7 +174,7 @@ class ApplyPage extends React.Component { isSubmitting: true, }); - //Send Application to backend + // Send Application to backend registerUser(this.props.match.params.eventAlias, values) .then(() => { // Log successful application with Google Analytics @@ -183,7 +183,7 @@ class ApplyPage extends React.Component { action: 'Successful', }); - //Show success page + // Show success page this.nextPage(); }) .catch((err) => { diff --git a/src/client/pages/DashboardPage/components/AdminDashboard.tsx b/src/client/pages/DashboardPage/components/AdminDashboard.tsx index fbb8fc6c..1e9609fd 100644 --- a/src/client/pages/DashboardPage/components/AdminDashboard.tsx +++ b/src/client/pages/DashboardPage/components/AdminDashboard.tsx @@ -7,10 +7,10 @@ import EventList from './EventList'; interface AdminDashboardProps { - //events that the admin is permitted to see + // events that the admin is permitted to see events: TESCEvent[]; - //The current user (aka admin) requesting the page + // The current user (aka admin) requesting the page user: JWTAdminAuthToken; } diff --git a/src/client/pages/DashboardPage/components/EventList.tsx b/src/client/pages/DashboardPage/components/EventList.tsx index cb255369..f567af67 100644 --- a/src/client/pages/DashboardPage/components/EventList.tsx +++ b/src/client/pages/DashboardPage/components/EventList.tsx @@ -6,13 +6,13 @@ import EventCard from '~/components/EventCard'; interface EventListProps { - //events to be rendered in the list + // events to be rendered in the list events: TESCEvent[]; - - //track if we need a direct link to the resume page + + // track if we need a direct link to the resume page resumeLink?: boolean; - //can this user create an event? + // can this user create an event? canCreate?: boolean; } diff --git a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx index 0a44a886..052e6d5b 100644 --- a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx +++ b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx @@ -5,12 +5,12 @@ import EventList from './EventList'; interface SponsorDashboardProps { - //events that this sponsor is allowed to see + // events that this sponsor is allowed to see events: TESCEvent[]; } /** - * This is the sponsor's event list. It uses EventList's resumeLink prop to + * This is the sponsor's event list. It uses EventList's resumeLink prop to * enforce a link to the sponsor portal on clicking the card. */ export default class SponsorDashboard extends React.Component { diff --git a/src/client/pages/DashboardPage/index.tsx b/src/client/pages/DashboardPage/index.tsx index 42d0049b..e48f2bf1 100644 --- a/src/client/pages/DashboardPage/index.tsx +++ b/src/client/pages/DashboardPage/index.tsx @@ -25,18 +25,18 @@ interface DashboardPageProps { } /** - * This component receives props in 3 ways - + * This component receives props in 3 ways - * 1) The explicit props provied to it by DashboardPageProps * 2) The redux state provided to it by mapStateToProps * 3) The dispatch functions provided to it by mapDispatchToProps - * + * * So, the props of this component is the union of the return types of mapStateToProps, * mapDispatchToProps and DashboardPageProps */ type Props = ReturnType & ReturnType & DashboardPageProps; /** - * This is the page that an admin sees when they first log into tesc.events. + * This is the page that an admin sees when they first log into tesc.events. * It shows the list of events that admin is permitted to see */ class DashboardPage extends React.Component { @@ -44,7 +44,7 @@ class DashboardPage extends React.Component { componentDidMount() { this.props.showLoading(); - //hide the loading state at the end of this promise + // hide the loading state at the end of this promise this.props.loadAllAdminEvents() .catch(console.error) .finally(this.props.hideLoading); diff --git a/src/client/pages/EventPage/components/AppsOverTimeStatistics.tsx b/src/client/pages/EventPage/components/AppsOverTimeStatistics.tsx index 0e0c6b55..f9346408 100644 --- a/src/client/pages/EventPage/components/AppsOverTimeStatistics.tsx +++ b/src/client/pages/EventPage/components/AppsOverTimeStatistics.tsx @@ -6,7 +6,7 @@ import EventStatisticsComponent from './EventStatisticsComponent'; export default class AppsOverTimeStatistics extends EventStatisticsComponent { render() { const { statistics } = this.props; - + // Create the data array needed to make the line chart const appsTimeData: Array<{ date: string; appCount: number }> = []; @@ -60,12 +60,12 @@ export default class AppsOverTimeStatistics extends EventStatisticsComponent { y="appCount" animate={{ duration: 1000, - easing: "cubic" + easing: 'cubic', }} style={{ data: { strokeWidth: 2, - strokeLinecap: "round" + strokeLinecap: 'round', }, }} /> diff --git a/src/client/pages/EventPage/components/BulkChange.tsx b/src/client/pages/EventPage/components/BulkChange.tsx index 6579a7c6..68e208be 100644 --- a/src/client/pages/EventPage/components/BulkChange.tsx +++ b/src/client/pages/EventPage/components/BulkChange.tsx @@ -4,10 +4,10 @@ import { Field, reduxForm, InjectedFormProps } from 'redux-form'; export interface BulkChangeFormData { - // new line separated + // new line separated users: string; - //new status to be set + // new status to be set status: string; } @@ -15,7 +15,7 @@ interface BulkChangeProps { } -//Use a union of BulkChangeProps and Redux Form's Props +// Use a union of BulkChangeProps and Redux Form's Props type Props = InjectedFormProps & BulkChangeProps; /** diff --git a/src/client/pages/EventPage/components/CustomQuestion.tsx b/src/client/pages/EventPage/components/CustomQuestion.tsx index 3b9ced6e..57f62f63 100644 --- a/src/client/pages/EventPage/components/CustomQuestion.tsx +++ b/src/client/pages/EventPage/components/CustomQuestion.tsx @@ -5,18 +5,18 @@ import ToggleSwitch from '~/components/ToggleSwitch'; interface CustomQuestionProps { - //the question this component describes + // the question this component describes question: Question; - //callback for the delete question button + // callback for the delete question button onDelete: () => void; - //callback for the questions' required switch toggle + // callback for the questions' required switch toggle onChangeRequired: (newRequired: boolean) => void; } /** - * This component renders a custom question on ./SettingsTab.tsx + * This component renders a custom question on ./SettingsTab.tsx * It handles the functionality for toggling the required field, and deleting the question. */ class CustomQuestion extends React.Component { diff --git a/src/client/pages/EventPage/components/EventOptionsEdit.tsx b/src/client/pages/EventPage/components/EventOptionsEdit.tsx index fa95ab4b..07be3758 100644 --- a/src/client/pages/EventPage/components/EventOptionsEdit.tsx +++ b/src/client/pages/EventPage/components/EventOptionsEdit.tsx @@ -5,17 +5,17 @@ import { UncontrolledTooltip } from 'reactstrap'; import ToggleSwitch from '~/components/ToggleSwitch'; interface EventOptionsProps { - //initial options of the event + // initial options of the event options: TESCEventOptions; - //calback function for when the update button is clicked + // calback function for when the update button is clicked onOptionsUpdate: (newOptions: TESCEventOptions) => void; - //the event these options are for + // the event these options are for event: TESCEvent; } -//the current, edited state of the options +// the current, edited state of the options interface EventOptionsState { options: TESCEventOptions; } @@ -33,8 +33,8 @@ export default class EventOptionsEdit extends React.Component () => { @@ -47,7 +47,7 @@ export default class EventOptionsEdit extends React.Component void; - //callback to add the organiser to the event + // callback to add the organiser to the event registerNewOrganiser: (newOrganiser: NewAdminModalFormData) => void; } interface OrganiserListState { - //the new organiser to be added to the event + // the new organiser to be added to the event newOrganiser: AdminSelectType; - //boolean to track if the new organiser modal is open or not + // boolean to track if the new organiser modal is open or not isRegisterModalOpen: boolean; } @@ -36,10 +36,9 @@ export default class OrganiserList extends React.Component @@ -60,7 +59,7 @@ export default class OrganiserList extends React.Component { diff --git a/src/client/pages/EventPage/components/PreviewApplication.tsx b/src/client/pages/EventPage/components/PreviewApplication.tsx index e8b73df7..0f1013a2 100644 --- a/src/client/pages/EventPage/components/PreviewApplication.tsx +++ b/src/client/pages/EventPage/components/PreviewApplication.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import ApplyPage from '../../ApplyPage'; import { Alert } from 'reactstrap'; +import ApplyPage from '../../ApplyPage'; + class PreviewApplication extends React.Component { render() { @@ -16,4 +17,4 @@ class PreviewApplication extends React.Component { } } -export default PreviewApplication; \ No newline at end of file +export default PreviewApplication; diff --git a/src/client/pages/EventPage/components/QuestionInput.tsx b/src/client/pages/EventPage/components/QuestionInput.tsx index 680988fe..138f461c 100644 --- a/src/client/pages/EventPage/components/QuestionInput.tsx +++ b/src/client/pages/EventPage/components/QuestionInput.tsx @@ -27,7 +27,7 @@ export default class QuestionInput extends React.Component { this.props.onAddQuestion(this.state); - //reset the component state + // reset the component state this.setState({ question: '', isRequired: false, diff --git a/src/client/pages/EventPage/components/SponsorList.tsx b/src/client/pages/EventPage/components/SponsorList.tsx index 9e94556a..ee7a252b 100644 --- a/src/client/pages/EventPage/components/SponsorList.tsx +++ b/src/client/pages/EventPage/components/SponsorList.tsx @@ -8,22 +8,22 @@ import SponsorSelect from '~/components/SponsorSelect'; interface SponsorListProps { - //the sponsors for this event + // the sponsors for this event sponsors: Admin[]; - //callback function to add an existing sponsor to the event + // callback function to add an existing sponsor to the event addNewSponsor: (toAdd: AdminSelectType) => void; - //function called to create a new sponsor in the system + // function called to create a new sponsor in the system registerNewSponsor: (newSponsor: NewAdminModalFormData) => void; } interface SponsorListState { - - //the new sponsor to be added + + // the new sponsor to be added newSponsor: AdminSelectType; - //boolean to track if the create sponsor modal is open or not. + // boolean to track if the create sponsor modal is open or not. isRegisterModalOpen: boolean; } @@ -38,7 +38,7 @@ export default class SponsorList extends React.Component @@ -59,7 +59,7 @@ export default class SponsorList extends React.Component { diff --git a/src/client/pages/EventPage/components/ViewApplication.tsx b/src/client/pages/EventPage/components/ViewApplication.tsx index af971097..f70a901f 100644 --- a/src/client/pages/EventPage/components/ViewApplication.tsx +++ b/src/client/pages/EventPage/components/ViewApplication.tsx @@ -1,18 +1,18 @@ +import { TESCEvent } from '@Shared/ModelTypes'; import React from 'react'; +import FA from 'react-fontawesome'; import { Link } from 'react-router-dom'; -import { TESCEvent } from '@Shared/ModelTypes'; import { UncontrolledTooltip } from 'reactstrap/lib/Uncontrolled'; -import FA from 'react-fontawesome'; type ViewApplicationProps = { - event: TESCEvent -} + event: TESCEvent; +}; class ViewApplication extends React.Component { render() { const {event} = this.props; - const isEventClosed = new Date(event.closeTime) < new Date + const isEventClosed = new Date(event.closeTime) < new Date; return ( <> {isEventClosed && { } } -export default ViewApplication; \ No newline at end of file +export default ViewApplication; diff --git a/src/client/pages/EventPage/index.tsx b/src/client/pages/EventPage/index.tsx index ff8c9e91..488c3846 100644 --- a/src/client/pages/EventPage/index.tsx +++ b/src/client/pages/EventPage/index.tsx @@ -11,6 +11,8 @@ import Loading from '~/components/Loading'; import { loadEventStatistics, loadAllTeams, editExistingEvent } from '~/data/AdminApi'; import { ApplicationState } from '~/reducers'; +import EventForm, { EventFormData } from '../../components/EventForm'; +import createValidator from '../NewEventPage/validate'; import TabularPage, { TabularPageState, TabularPageProps, TabPage, TabularPageNav } from '../TabularPage'; import { @@ -19,18 +21,16 @@ import { } from './actions'; import CheckinStatistics from './components/CheckinStatistics'; import ResumeStatistics from './components/ResumeStatistics'; +import ViewApplication from './components/ViewApplication'; import ActionsTab from './tabs/ActionsTab'; import AdministratorsTab from './tabs/AdministratorsTab'; import SettingsTab from './tabs/SettingsTab'; import StatisticsTab from './tabs/StatisticsTab'; import TeamsTab from './tabs/TeamsTab'; -import ViewApplication from './components/ViewApplication'; -import EventForm, { EventFormData } from '../../components/EventForm'; -import createValidator from '../NewEventPage/validate'; type RouteProps = RouteComponentProps<{ - //the eventAlias for the event we want to render the dashboard for + // the eventAlias for the event we want to render the dashboard for eventAlias: string; }>; @@ -62,11 +62,11 @@ interface EventPageProps extends TabularPageProps { } /** - * This component receives props in 3 ways - + * This component receives props in 3 ways - * 1) The explicit props provied to it by EventPageProps * 2) The redux state provided to it by mapStateToProps * 3) The dispatch functions provided to it by mapDispatchToProps - * + * * So, the props of this component is the union of the return types of mapStateToProps, * mapDispatchToProps and EventPageProps */ @@ -80,7 +80,7 @@ interface EventPageState extends TabularPageState { /** * This page renders the main page for an event. * It has tabs and links to the other pages related to this event. - * + * * This component is extending from TabularPage, which has the tabbing functionality abstracted away */ class EventPage extends TabularPage { @@ -245,7 +245,7 @@ class EventPage extends TabularPage { closeTimeMonth: eventDate.getMonth(), closeTimeYear: eventDate.getFullYear(), logo: undefined, - } + }; const editEvent = async (eventData: EventFormData) => { try { @@ -258,12 +258,12 @@ class EventPage extends TabularPage { } catch (e) { this.props.addEventDangerAlert(eventData.alias, e.message, 'Edit Event'); } - } + }; return (
{ @@ -22,9 +22,9 @@ export default class ActionsTab extends EventPageTab { exportUsers = () => { const eventAlias = this.props.event.alias; - //call API's exportUsers. - //surprisingly, this is okay syntax because exportUsers and this.exportUsers are different - //TODO: name API call better + // call API's exportUsers. + // surprisingly, this is okay syntax because exportUsers and this.exportUsers are different + // TODO: name API call better exportUsers(eventAlias, false) .end((err, res) => { // Download as file @@ -50,7 +50,7 @@ export default class ActionsTab extends EventPageTab { // Split users into array const usersSplit = users.split(/\n/); - //call API's bulk change + // call API's bulk change bulkChange(usersSplit, status) .then(() => { this.props.addEventSuccessAlert(event.alias, 'Successfully updated users!', 'Bulk Change'); @@ -82,7 +82,7 @@ export default class ActionsTab extends EventPageTab { > Export User Emails - +
@@ -92,4 +92,4 @@ export default class ActionsTab extends EventPageTab {
); } -} \ No newline at end of file +} diff --git a/src/client/pages/EventPage/tabs/AdministratorsTab.tsx b/src/client/pages/EventPage/tabs/AdministratorsTab.tsx index d64b82f8..d959a40c 100644 --- a/src/client/pages/EventPage/tabs/AdministratorsTab.tsx +++ b/src/client/pages/EventPage/tabs/AdministratorsTab.tsx @@ -13,16 +13,16 @@ interface AdministratorsTabProps { interface AdminReference { - //the database ID of an admin + // the database ID of an admin _id: string; - //the username of an admin + // the username of an admin username: string; } /** * View Administrators in this event - * + * * This tab currently has: * - View current organizers * - View current sponsors diff --git a/src/client/pages/EventPage/tabs/SettingsTab.tsx b/src/client/pages/EventPage/tabs/SettingsTab.tsx index cdcf4265..0b79052d 100644 --- a/src/client/pages/EventPage/tabs/SettingsTab.tsx +++ b/src/client/pages/EventPage/tabs/SettingsTab.tsx @@ -14,14 +14,14 @@ import EventPageTab from './EventPageTab'; interface SettingsTabProps { } -//TODO: not sure why this exists as opposed to a boolean? +// TODO: not sure why this exists as opposed to a boolean? interface SettingsTabState { customQuestionsRequests: number; } /** * This is the settings tag for an event. This tab currently has: - * + * * - Toggling event options * - Add custom question * - Update custom question @@ -51,7 +51,7 @@ export default class SettingsTab extends EventPageTab { @@ -71,7 +71,7 @@ export default class SettingsTab extends EventPageTab { /** * Send the Forgot Password Email - * + * * @param {ForgotFormData} values the forgot form data */ sendForgotPassword = (values: ForgotFormData) => { diff --git a/src/client/pages/HomePage/components/UserEvents.tsx b/src/client/pages/HomePage/components/UserEvents.tsx index 6c1f8e1a..c88fcc41 100644 --- a/src/client/pages/HomePage/components/UserEvents.tsx +++ b/src/client/pages/HomePage/components/UserEvents.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; interface UserEventsProps { - //the events that the user has applied to + // the events that the user has applied to events: TESCEvent[]; } diff --git a/src/client/pages/HomePage/index.tsx b/src/client/pages/HomePage/index.tsx index 23642159..712a2125 100644 --- a/src/client/pages/HomePage/index.tsx +++ b/src/client/pages/HomePage/index.tsx @@ -32,7 +32,7 @@ interface HomePageProps { type Props = ReturnType & ReturnType & HomePageProps; /** - * This is the main tesc.event homepage. If the user requesting it is authenticated, it will show + * This is the main tesc.event homepage. If the user requesting it is authenticated, it will show * the user their existing applications. If the user is not authenticated, it will show all open applications */ class HomePage extends React.Component { @@ -60,7 +60,7 @@ class HomePage extends React.Component { /** * Renders the user's existing applications - * + * * @param {TESCEvent[]} events the events that this user has applied to */ userEvents(events: TESCEvent[]) { @@ -73,7 +73,7 @@ class HomePage extends React.Component { /** * Renders the current events that are open for applications - * + * * @param {TESCEvent[]} events the events to be rendered * @param {Boolean} small display mode for the event cards */ @@ -88,7 +88,7 @@ class HomePage extends React.Component { render() { const { events, userEvents } = this.props; - //show the sidebar if the authenticated user has any applications + // show the sidebar if the authenticated user has any applications const showSidebar = Object.values(userEvents).length > 0; let currentEvents: TESCEvent[] = []; diff --git a/src/client/pages/NewEventPage/index.tsx b/src/client/pages/NewEventPage/index.tsx index da8ad79e..bc44ae34 100644 --- a/src/client/pages/NewEventPage/index.tsx +++ b/src/client/pages/NewEventPage/index.tsx @@ -6,11 +6,11 @@ import { withRouter, RouteComponentProps } from 'react-router'; import { UncontrolledAlert } from 'reactstrap'; import { bindActionCreators } from 'redux'; import { ApplicationDispatch } from '~/actions'; +import EventForm, { EventFormData } from '~/components/EventForm'; import { registerNewEvent } from '~/data/AdminApi'; import { addEventSuccessAlert } from '../EventPage/actions'; -import EventForm, { EventFormData } from '~/components/EventForm'; import createValidator from './validate'; const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators({ @@ -38,12 +38,12 @@ class NewEventPage extends React.Component { /** * Create a new event in the system - * + * * @param {NewEventFormData} event the event to be created */ createNewEvent = (event: EventFormData) => { - //send event to API + // send event to API registerNewEvent(event) .then((res: TESCEvent) => { this.setState({ err: null }); diff --git a/src/client/pages/NewEventPage/validate.ts b/src/client/pages/NewEventPage/validate.ts index 667272db..b539bd25 100644 --- a/src/client/pages/NewEventPage/validate.ts +++ b/src/client/pages/NewEventPage/validate.ts @@ -7,7 +7,7 @@ const createValidator = (requireLogo = true, allowPastDates = false) => (values: const required = ['name', 'alias', 'closeTimeMonth', 'closeTimeDay', 'closeTimeYear', 'email', 'homepage', 'description']; - requireLogo && required.push('logo') + requireLogo && required.push('logo'); const notValid = required.filter(name => !(name in values) || !values[name]); notValid.forEach(name => errors[name] = 'Required'); diff --git a/src/client/pages/ResumesPage/components/ResumeList.tsx b/src/client/pages/ResumesPage/components/ResumeList.tsx index 0bd57c09..0a45f459 100644 --- a/src/client/pages/ResumesPage/components/ResumeList.tsx +++ b/src/client/pages/ResumesPage/components/ResumeList.tsx @@ -4,13 +4,13 @@ import ToggleSwitch from '~/components/ToggleSwitch'; interface ResumeListProps { - //callback for when the compact state toggle is clicked + // callback for when the compact state toggle is clicked onCompactChange: () => void; - //the compacted state + // the compacted state isCompacted: boolean; - //the users to render + // the users to render applicants: TESCUser[]; } @@ -20,7 +20,7 @@ interface ColumnMap { interface ResumeListState { - //columns shown on screen + // columns shown on screen columns: ColumnMap; smallColumns: string[]; mediumColumns: string[]; diff --git a/src/client/pages/ResumesPage/index.tsx b/src/client/pages/ResumesPage/index.tsx index dcb77739..9361db41 100644 --- a/src/client/pages/ResumesPage/index.tsx +++ b/src/client/pages/ResumesPage/index.tsx @@ -1,23 +1,23 @@ import { TESCUser } from '@Shared/ModelTypes'; -import { UserStatus } from '@Shared/UserStatus'; -import React from 'react'; -import { connect } from 'react-redux'; -import { showLoading, hideLoading } from 'react-redux-loading-bar'; -import { RouteComponentProps } from 'react-router'; -import { bindActionCreators } from 'redux'; -import { ApplicationDispatch, loadAllAdminEvents } from '~/actions'; -import { loadAllSponsorUsers } from '~/data/AdminApi'; -import { ApplicationState } from '~/reducers'; -import { applyResumeFilter } from '~/static/Resumes'; - -import { replaceApplicants, replaceFiltered } from './actions'; -import ResumeList from './components/ResumeList'; - -type RouteProps = RouteComponentProps<{ + import { UserStatus } from '@Shared/UserStatus'; + import React from 'react'; + import { connect } from 'react-redux'; + import { showLoading, hideLoading } from 'react-redux-loading-bar'; + import { RouteComponentProps } from 'react-router'; + import { bindActionCreators } from 'redux'; + import { ApplicationDispatch, loadAllAdminEvents } from '~/actions'; + import { loadAllSponsorUsers } from '~/data/AdminApi'; + import { ApplicationState } from '~/reducers'; + import { applyResumeFilter } from '~/static/Resumes'; + + import { replaceApplicants, replaceFiltered } from './actions'; + import ResumeList from './components/ResumeList'; + + type RouteProps = RouteComponentProps<{ eventAlias: string; }>; -const mapStateToProps = (state: ApplicationState, ownProps: RouteProps) => { + const mapStateToProps = (state: ApplicationState, ownProps: RouteProps) => { const eventAlias = ownProps.match.params.eventAlias; return { event: state.admin.events[eventAlias], @@ -27,7 +27,7 @@ const mapStateToProps = (state: ApplicationState, ownProps: RouteProps) => { }; }; -const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators({ + const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators({ replaceApplicants, showLoading, hideLoading, @@ -35,25 +35,25 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators loadAllAdminEvents, }, dispatch); -interface ResumesPageProps { + interface ResumesPageProps { } -// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// the props of this event are the union of the react-router data, redux actions and dispatch, and the // regular props of the component -type Props = RouteComponentProps<{ + type Props = RouteComponentProps<{ eventAlias: string; }> & ReturnType & ReturnType & ResumesPageProps; -interface ResumesPageState { + interface ResumesPageState { - //boolean to track compact state + // boolean to track compact state isCompacted: boolean; } /** * This is the sponsor tool that shows a list of applicants to an event and their resumes */ -class ResumesPage extends React.Component { + class ResumesPage extends React.Component { state: Readonly = { isCompacted: false, }; @@ -112,4 +112,4 @@ class ResumesPage extends React.Component { } } -export default connect(mapStateToProps, mapDispatchToProps)(ResumesPage); + export default connect(mapStateToProps, mapDispatchToProps)(ResumesPage); diff --git a/src/client/pages/UserPage/components/BussingModal.tsx b/src/client/pages/UserPage/components/BussingModal.tsx index 53fdd2c2..8a044d1a 100644 --- a/src/client/pages/UserPage/components/BussingModal.tsx +++ b/src/client/pages/UserPage/components/BussingModal.tsx @@ -3,13 +3,13 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface BussingModalProps { - //boolean to track if the modal is open + // boolean to track if the modal is open isOpen: boolean; - //is a bus available for this school? + // is a bus available for this school? availableBus?: string; - //callback for when the bus is clicked + // callback for when the bus is clicked onChooseBus: (choice: boolean) => void; } @@ -20,7 +20,7 @@ export default class BussingModal extends React.Component { /** * Callback for the bus choose button - * + * * @param {Boolean} bussing boolean to track bussing status */ onChooseBus = (bussing: boolean) => () => diff --git a/src/client/pages/UserPage/components/RSVPConfirm.tsx b/src/client/pages/UserPage/components/RSVPConfirm.tsx index ba9057ca..2e0c27de 100644 --- a/src/client/pages/UserPage/components/RSVPConfirm.tsx +++ b/src/client/pages/UserPage/components/RSVPConfirm.tsx @@ -25,7 +25,7 @@ export default class RSVPConfirm extends React.Component this.setState({page: this.state.page + 1}); onChooseStatus = (status: boolean) => { @@ -48,7 +48,7 @@ export default class RSVPConfirm extends React.Component { diff --git a/src/client/pages/UserPage/components/RSVPModal.tsx b/src/client/pages/UserPage/components/RSVPModal.tsx index 602f53a7..e213845e 100644 --- a/src/client/pages/UserPage/components/RSVPModal.tsx +++ b/src/client/pages/UserPage/components/RSVPModal.tsx @@ -4,16 +4,16 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface RSVPModalProps { - //toggle function for the model + // toggle function for the model toggle: () => void; - //variable to track open or close state of the modal + // variable to track open or close state of the modal isOpen: boolean; - //callback function for when the status is chosen + // callback function for when the status is chosen onChooseStatus: (statusChoice: boolean) => void; - //the event for which the current application is on + // the event for which the current application is on event: TESCEvent; } @@ -21,7 +21,7 @@ export default class RSVPModal extends React.Component { /** * Callback for when an RSVP status is chosen - * + * * @param {Boolean} status RSVP true or false */ onChooseStatus = (status: boolean) => () => this.props.onChooseStatus(status); diff --git a/src/client/pages/UserPage/components/UserProfile.tsx b/src/client/pages/UserPage/components/UserProfile.tsx index c7109488..b19193b4 100644 --- a/src/client/pages/UserPage/components/UserProfile.tsx +++ b/src/client/pages/UserPage/components/UserProfile.tsx @@ -1,13 +1,13 @@ import { TESCUser, TESCEvent, TESCTeam } from '@Shared/ModelTypes'; -import { UserStatus } from '@Shared/UserStatus'; +import { generateQRCodeURL } from '@Shared/QRCodes'; import { UserGenderOptions, UserPronounOptions, UserShirtSizeOptions } from '@Shared/UserEnums'; +import { UserStatus } from '@Shared/UserStatus'; +import { JSXElement } from 'babel-types'; import React from 'react'; +import FA from 'react-fontawesome'; import { Field, reduxForm, InjectedFormProps, WrappedFieldProps } from 'redux-form'; import { CustomFieldProps } from '~/components/Fields'; import FileField from '~/components/FileField'; -import { generateQRCodeURL } from '@Shared/QRCodes'; -import FA from 'react-fontawesome'; -import { JSXElement } from 'babel-types'; export interface UserProfileFormData { gender: string; @@ -24,14 +24,14 @@ export interface UserProfileFormData { } interface UserProfileProps { - - //the user for which the profile is rendered + + // the user for which the profile is rendered user: TESCUser; - //the event for which the application is on + // the event for which the application is on event: TESCEvent; - //callback function to toggle RSVP status + // callback function to toggle RSVP status toggleRSVP: () => void; } @@ -39,13 +39,13 @@ type Props = InjectedFormProps & UserProf /** * This is the component that shows the user their data on the application page - * + * * It also provides functionality to edit their application */ class UserProfile extends React.Component { /** * Render the gender selection. - * + * * @param {Object} _ an object with an input and className field * @returns {React.StatelessComponent} */ @@ -57,10 +57,10 @@ class UserProfile extends React.Component { ); }; - + /** * Render the T-Shirt size selection. - * + * * @param {Object} _ an object with an input and className field * @returns {React.StatelessComponent} */ @@ -188,9 +188,9 @@ class UserProfile extends React.Component { /** * Render the phone number and santitize it - * + * * @param {String} phone the phone number string - * @returns {String} + * @returns {String} */ renderPhoneNumber = (phone: string) => ( phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3') @@ -198,7 +198,7 @@ class UserProfile extends React.Component { /** * Render the Applicant's info - * + * * @param {TESCUser} user the info of the applicant * @returns {JSXElement} */ @@ -267,7 +267,7 @@ class UserProfile extends React.Component { /** * Render a user's preference section. - * + * * @param {TESCUser} user the user's data * @returns {JSXElement} */ diff --git a/src/client/pages/UserPage/index.tsx b/src/client/pages/UserPage/index.tsx index bd5e2129..c215f767 100644 --- a/src/client/pages/UserPage/index.tsx +++ b/src/client/pages/UserPage/index.tsx @@ -33,7 +33,7 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface UserPageProps { } -// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// the props of this event are the union of the react-router data, redux actions and dispatch, and the // regular props of the component type Props = RouteComponentProps<{ eventAlias: string; diff --git a/src/client/pages/UsersPage/components/ColumnEditor.tsx b/src/client/pages/UsersPage/components/ColumnEditor.tsx index 71260659..5ed97aa2 100644 --- a/src/client/pages/UsersPage/components/ColumnEditor.tsx +++ b/src/client/pages/UsersPage/components/ColumnEditor.tsx @@ -83,4 +83,4 @@ export default class UserList extends React.Component ); } -} \ No newline at end of file +} diff --git a/src/client/pages/UsersPage/components/UserList.tsx b/src/client/pages/UsersPage/components/UserList.tsx index 13e147bb..28d78fae 100644 --- a/src/client/pages/UsersPage/components/UserList.tsx +++ b/src/client/pages/UsersPage/components/UserList.tsx @@ -11,7 +11,7 @@ const styles = require('react-table/react-table.css'); interface UserListProps { - //users that have applied to an event + // users that have applied to an event users: TESCUser[]; columns: AutofillColumn[]; event: TESCEvent; @@ -26,7 +26,7 @@ class UserList extends React.Component { /** * Render the User component, used by react-table's SubComponent - * + * * @param {TESCUser} row the user to be rendered * @returns {JSXElement} */ @@ -49,7 +49,7 @@ class UserList extends React.Component { data={this.props.users} column={{ ...ReactTableDefaults.column, - Cell: ({value}) => value ? String(value): value + Cell: ({value}) => value ? String(value): value, }} columns={this.props.columns} defaultPageSize={10} diff --git a/src/client/pages/UsersPage/index.tsx b/src/client/pages/UsersPage/index.tsx index e6612f3a..22a33e5f 100644 --- a/src/client/pages/UsersPage/index.tsx +++ b/src/client/pages/UsersPage/index.tsx @@ -44,7 +44,7 @@ type RouteProps = RouteComponentProps<{ eventAlias: string; }>; -//the props of this page is the union of the react-router, redux and explicit props +// The props of this page is the union of the react-router, redux and explicit props type Props = RouteProps & ReturnType & ReturnType & UsersPageProps; interface UsersPageState extends AlertPageState { @@ -132,7 +132,7 @@ class UsersPage extends AlertPage { /** * Handles an update to a user in the list. - * + * * @param {TESCUser} user the user to be updated */ onUserUpdate = (user: TESCUser) => { diff --git a/src/client/routes.tsx b/src/client/routes.tsx index 91ff0028..2f197391 100644 --- a/src/client/routes.tsx +++ b/src/client/routes.tsx @@ -13,34 +13,24 @@ import { authorised as AdminAuthorised } from '~/data/AdminApi'; import { authorised as UserAuthorised } from '~/data/UserApi'; import CookieTypes from '~/static/Cookies'; -/* - PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around - react-router-dom's Route component to handle authentication state. -*/ import PrivateRoute from './PrivateRoute'; import PrivateUserRoute from './PrivateUserRoute'; - -//Authentication Components & Actions -//TODO: Document better import { ApplicationDispatch } from './actions'; import AdminLogout from './auth/admin/Logout'; import { finishAuthorisation, authoriseAdmin, logoutAdmin } from './auth/admin/actions'; import ConfirmPage from './auth/user/Confirm'; import UserLogout from './auth/user/Logout'; import { authoriseUser, finishAuthorisation as finishUserAuth, logoutUser } from './auth/user/actions'; - -//Importing the different layouts (page structures) for the application import AdminLayout from './layouts/admin'; -import SponsorLayout from './layouts/sponsor'; import PublicLayout from './layouts/public'; +import SponsorLayout from './layouts/sponsor'; import UserLayout from './layouts/user'; - -//Importing all the pages for the app, used later when setting up routes. import AdminsPage from './pages/AdminsPage'; import ApplyPage from './pages/ApplyPage'; import CheckinPage from './pages/CheckinPage'; import Dashboard from './pages/DashboardPage'; import EventPage from './pages/EventPage'; +import PreviewApplication from './pages/EventPage/components/PreviewApplication'; import ForgotPage from './pages/ForgotPage'; import HomePage from './pages/HomePage'; import LoginPage from './pages/LoginPage'; @@ -50,7 +40,18 @@ import ResetPage from './pages/ResetPage'; import ResumesPage from './pages/ResumesPage'; import UserPage from './pages/UserPage'; import UsersPage from './pages/UsersPage'; -import PreviewApplication from './pages/EventPage/components/PreviewApplication'; + +/* + PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around + react-router-dom's Route component to handle authentication state. +*/ + +// Authentication Components & Actions +// TODO: Document better + +// Importing the different layouts (page structures) for the application + +// Importing all the pages for the app, used later when setting up routes. const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators({ authoriseAdmin, @@ -167,7 +168,7 @@ class Routes extends React.Component { } renderPublic = (RenderComponent: any) => { - return (props: RouteComponentProps) => + return (props: RouteComponentProps) => ( diff --git a/src/shared/QRCodes.ts b/src/shared/QRCodes.ts index 74e80741..40f62f53 100644 --- a/src/shared/QRCodes.ts +++ b/src/shared/QRCodes.ts @@ -1,10 +1,10 @@ import { TESCUser } from '@Shared/ModelTypes'; const QR_CODE_SIZE = 200; -const QR_CODE_API_ROOT = `https://api.qrserver.com/v1/create-qr-code/` +const QR_CODE_API_ROOT = `https://api.qrserver.com/v1/create-qr-code/`; function generateQRCodeURL(user: TESCUser) { - return `${QR_CODE_API_ROOT}?size=${QR_CODE_SIZE}x${QR_CODE_SIZE}&data=${user._id}` + return `${QR_CODE_API_ROOT}?size=${QR_CODE_SIZE}x${QR_CODE_SIZE}&data=${user._id}`; } -export { generateQRCodeURL } \ No newline at end of file +export { generateQRCodeURL }; diff --git a/src/shared/api/Requests.ts b/src/shared/api/Requests.ts index 060774e7..4ba587ef 100644 --- a/src/shared/api/Requests.ts +++ b/src/shared/api/Requests.ts @@ -142,4 +142,4 @@ export interface RSVPUserRequest { export interface StatusEmailRequest { user: TESCUser; -} \ No newline at end of file +} diff --git a/src/shared/api/Responses.ts b/src/shared/api/Responses.ts index dd897ca9..2bdac334 100644 --- a/src/shared/api/Responses.ts +++ b/src/shared/api/Responses.ts @@ -14,7 +14,7 @@ export interface EventStatistics { resumes: number; appsOverTime: { [Date: string]: number; - } + }; } export type EventUserCounts = { diff --git a/tslint.json b/tslint.json index 1be00e29..848e24ed 100644 --- a/tslint.json +++ b/tslint.json @@ -15,7 +15,7 @@ ], "comment-format": [ true, - "check-space" + "check-space", ], "forin": false, "import-blacklist": [ From 8437aea51f4f5cd001abc4f7f4f2cffd273715b8 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 15:51:36 -0700 Subject: [PATCH 15/31] Added root-level frontend README --- src/client/README.md | 11 +++++++++++ src/client/main.tsx | 1 + src/client/pages/README.md | 1 + src/client/routes.tsx | 18 +++++++++++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/client/README.md create mode 100644 src/client/pages/README.md diff --git a/src/client/README.md b/src/client/README.md new file mode 100644 index 00000000..f158a420 --- /dev/null +++ b/src/client/README.md @@ -0,0 +1,11 @@ +## src/client - Frontend + +This directory is the home for the frontend for tesc.events. + +tesc.events' frontend is written entirely in [React.js](https://reactjs.org/). It was initially written in JavaScript, but [was ported over to TypeScript in April 2019](https://github.com/UCSDTESC/Check-in/pull/131) + +The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/main.tsx) + +Routes for the application (/user/, /admin/ etc.) are set up in [routes.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/routes.tsx) + +The main application code lies in [pages/](https://github.com/UCSDTESC/Check-in/blob/master/src/client/pages) diff --git a/src/client/main.tsx b/src/client/main.tsx index 17f91144..3b0776ca 100644 --- a/src/client/main.tsx +++ b/src/client/main.tsx @@ -22,6 +22,7 @@ const store = createStore(reducer, applyMiddleware(reduxThunk as ThunkMiddleware) )); +//Configuring the application for routing, cookies, and redux. render( ( diff --git a/src/client/pages/README.md b/src/client/pages/README.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/client/pages/README.md @@ -0,0 +1 @@ + diff --git a/src/client/routes.tsx b/src/client/routes.tsx index edfa1a4d..91ff0028 100644 --- a/src/client/routes.tsx +++ b/src/client/routes.tsx @@ -13,18 +13,29 @@ import { authorised as AdminAuthorised } from '~/data/AdminApi'; import { authorised as UserAuthorised } from '~/data/UserApi'; import CookieTypes from '~/static/Cookies'; +/* + PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around + react-router-dom's Route component to handle authentication state. +*/ import PrivateRoute from './PrivateRoute'; import PrivateUserRoute from './PrivateUserRoute'; + +//Authentication Components & Actions +//TODO: Document better import { ApplicationDispatch } from './actions'; import AdminLogout from './auth/admin/Logout'; import { finishAuthorisation, authoriseAdmin, logoutAdmin } from './auth/admin/actions'; import ConfirmPage from './auth/user/Confirm'; import UserLogout from './auth/user/Logout'; import { authoriseUser, finishAuthorisation as finishUserAuth, logoutUser } from './auth/user/actions'; + +//Importing the different layouts (page structures) for the application import AdminLayout from './layouts/admin'; import SponsorLayout from './layouts/sponsor'; import PublicLayout from './layouts/public'; import UserLayout from './layouts/user'; + +//Importing all the pages for the app, used later when setting up routes. import AdminsPage from './pages/AdminsPage'; import ApplyPage from './pages/ApplyPage'; import CheckinPage from './pages/CheckinPage'; @@ -140,6 +151,12 @@ class Routes extends React.Component { ); } + /** + * Render a route with the User layout. + * @param {JSX.IntrinsicElements} component The child component to render within the + * layout. + * @returns {Component} + */ renderUser = (RenderComponent: any) => { return (props: RouteComponentProps) => ( @@ -213,7 +230,6 @@ class Routes extends React.Component { /> {/* User Routes */} - Date: Sat, 18 May 2019 16:19:35 -0700 Subject: [PATCH 16/31] Tree --- src/client/README.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/client/README.md b/src/client/README.md index f158a420..9961e775 100644 --- a/src/client/README.md +++ b/src/client/README.md @@ -6,6 +6,35 @@ tesc.events' frontend is written entirely in [React.js](https://reactjs.org/). I The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/main.tsx) -Routes for the application (/user/, /admin/ etc.) are set up in [routes.tsx](https://github.com/UCSDTESC/Check-in/blob/master/src/client/routes.tsx) -The main application code lies in [pages/](https://github.com/UCSDTESC/Check-in/blob/master/src/client/pages) +### Directory Tree +``` +├── PrivateRoute.tsx + * Wrapper Component around react-router-dom's Route to handle rendering only if the user requesting the page is authenticated. Used on admin-side routes. +├── PrivateUserRoute.tsx + * Wrapper Component around react-router-dom's Route to handle rendering only if the user requesting the page is authenticated. Used on user-side routes. +├── README.md + * 😊 +├── actions + * This directory holds application-level [Redux Actions](https://redux.js.org/basics/actions). +├── auth + * This directory holds the components, [Redux Actions](https://redux.js.org/basics/actions) and [Redux Reducers](https://redux.js.org/basics/reducers) related to the login/logout functionality of both users and admins. +├── components + * Components that are required "globally" or in multiple places in the application are put here. Things like the Navbar, Footer, iOS Switch, Loading spinners etc. go here. +├── data + * The `data/` directory holds `Api.ts` and `User.ts`, which provide the application with methods to make API calls to our backend. +├── layouts + * A Layout defines the way our application looks. The application has different layouts depending on what kind of page you are looking at - admins and sponsors have sidebars, and users dont. Layouts are linked to a specific route in the `routes.tsx` file. +├── main.tsx + * This is the entrypoint to our application. It sets up our app to be used with Redux, react-router and Cookies and makes the intial React.Component.render() call. +├── pages + * Each `XYZPage` in the `pages` directory is linked to a specific page of the app. Each page is it’s own directory with an `index.tsx` file in it that defines that page. Each page can also define Redux Actions, Reducers and Components that it will use in `XYZPage/actions`, `XYZPage/reducers` and `XYZPage/components +├── reducers + * This directory holds application-level [Redux Reducers](https://redux.js.org/basics/reducers). +├── routes.tsx + * This directory defines react-router-dom's routes for the application. +├── static + * This directory holds "constant" data that the application needs - a list of universities, majors and so on. +└── typings + * TypeScript type definitions for JavaScript packages used in this application that are directly supplied by us - not by node_modules +``` \ No newline at end of file From 8f5f642ee9ab86780af67a53c59ac30bd7599f4b Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 16:23:48 -0700 Subject: [PATCH 17/31] Removed links --- src/client/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/README.md b/src/client/README.md index 9961e775..e9e90964 100644 --- a/src/client/README.md +++ b/src/client/README.md @@ -16,9 +16,9 @@ The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-i ├── README.md * 😊 ├── actions - * This directory holds application-level [Redux Actions](https://redux.js.org/basics/actions). + * This directory holds application-level Redux Actions. ├── auth - * This directory holds the components, [Redux Actions](https://redux.js.org/basics/actions) and [Redux Reducers](https://redux.js.org/basics/reducers) related to the login/logout functionality of both users and admins. + * This directory holds the components, Redux Actions and Redux Reducers related to the login/logout functionality of both users and admins. ├── components * Components that are required "globally" or in multiple places in the application are put here. Things like the Navbar, Footer, iOS Switch, Loading spinners etc. go here. ├── data @@ -30,7 +30,7 @@ The entry point to the code is in [main.tsx](https://github.com/UCSDTESC/Check-i ├── pages * Each `XYZPage` in the `pages` directory is linked to a specific page of the app. Each page is it’s own directory with an `index.tsx` file in it that defines that page. Each page can also define Redux Actions, Reducers and Components that it will use in `XYZPage/actions`, `XYZPage/reducers` and `XYZPage/components ├── reducers - * This directory holds application-level [Redux Reducers](https://redux.js.org/basics/reducers). + * This directory holds application-level Redux Reducers. ├── routes.tsx * This directory defines react-router-dom's routes for the application. ├── static From e35f0c28db4cc8ebbb24b2508303f5ef45dc2b3c Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 17:19:25 -0700 Subject: [PATCH 18/31] Added pages documentation --- src/client/pages/README.md | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/client/pages/README.md b/src/client/pages/README.md index 8b137891..2c3375c2 100644 --- a/src/client/pages/README.md +++ b/src/client/pages/README.md @@ -1 +1,43 @@ +## src/client/pages - Pages. Pages Everywhere. +An `XYZPage` in this directory implements a page in the application. Any `XYZPage` can define redux actions in `XYZPage/actions`, reducers in `XYZPage/reducers` or components it uses in `XYZPage/components`. + +``` +├── AdminsPage + * [Admin with role Developer only] It shows a list of all admins in the system. +├── AlertPage.tsx + * This is an abstraction that allows a page to have "alerts" at the top of it. An `XYZPage` can extend this class to implement alerts. +├── ApplyPage + * This is the hackathon registration page. +├── CheckinPage + * [Admin only] This page is the QR Code Checkin system for checkin into an event. +├── DashboardPage + * [Admin only] This page is the "opening" dashboard for an admin when they log into the system. + * It decides what to show to the admin depending on their role and the API response +├── EventPage + * [Admin only] This is the main dashboard for an event. It has tabs (extends `TabularPage.tsx`) for Actions, Administrators, Settings and Statistics +├── ForgotPage.tsx + * This page implements the forgot password functionality for users. +├── HomePage + * This is the main page that loads when a user goes to www.tesc.events + * This page handles 2 states + - showing all apply-able events when the user is not logged in, and show + - showing all existing applications when the user is logged in +├── LoginPage.tsx + * This page shows a username and password field for a user to login. +├── NewEventPage + * [Admin only] This page allows an admin to create a new event. +├── NotFound.tsx + * This is the 404 page. react-router is set up to show this when none of the routes are rendered. +├── ResetPage.tsx + * [User only] This page allows a user to reset their password (linked from a password reset email) +├── ResumesPage + * [Admin only] This is the sponsor-tool. It provides a dashboard with sorting / filtering features that is given to sponsors who pay for access to tesc.events +├── TabularPage.tsx + * This is an abstraction that lets a page that extends it implement tabs. +├── UserPage + * [User only] This is the page that lets a user view / edit their hackathon application +└── UsersPage + * [Admin only] This is the page that shows admins a list of users that have applied to an event and lets them update the application if needed. + * This page also has extensive sorting features for admins. +``` \ No newline at end of file From f25ae320b2dd8f3bff8743776c3e81cf0d788dfb Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 19:07:45 -0700 Subject: [PATCH 19/31] Documentation for AdminsPage and ApplyPage done --- src/client/pages/AdminsPage/actions/index.ts | 4 ++- .../pages/AdminsPage/components/AdminList.tsx | 7 +++++ src/client/pages/AdminsPage/index.tsx | 30 +++++++++++++++++++ .../pages/AdminsPage/reducers/Admins.ts | 1 + src/client/pages/AlertPage.tsx | 12 +++++++- .../ApplyPage/components/ApplyPageSection.tsx | 9 ++++++ .../pages/ApplyPage/components/Header.tsx | 6 ++++ .../ApplyPage/components/PersonalSection.tsx | 18 +++++++++++ .../ApplyPage/components/ResponseSection.tsx | 7 ++++- .../ApplyPage/components/UniversityField.tsx | 6 ++++ src/client/pages/ApplyPage/index.tsx | 24 +++++++++++++++ src/client/pages/README.md | 1 + 12 files changed, 122 insertions(+), 3 deletions(-) diff --git a/src/client/pages/AdminsPage/actions/index.ts b/src/client/pages/AdminsPage/actions/index.ts index 10dc9065..ad5d28ae 100644 --- a/src/client/pages/AdminsPage/actions/index.ts +++ b/src/client/pages/AdminsPage/actions/index.ts @@ -3,6 +3,8 @@ import { createStandardAction } from 'typesafe-actions'; import * as Types from './types'; -// Admins +// TODO: Not sure what this action does export const addAdmins = createStandardAction(Types.ADD_ADMINS)(); + +// Replace the `admins` state with the payload export const replaceAdmins = createStandardAction(Types.REPLACE_ADMINS)(); diff --git a/src/client/pages/AdminsPage/components/AdminList.tsx b/src/client/pages/AdminsPage/components/AdminList.tsx index 31370393..61df617b 100644 --- a/src/client/pages/AdminsPage/components/AdminList.tsx +++ b/src/client/pages/AdminsPage/components/AdminList.tsx @@ -4,8 +4,15 @@ import React from 'react'; import { Button } from 'reactstrap'; interface AdminListProps { + + //the admins in the system admins: Admin[]; + + //function to be called when the delete button is pressed onDeleteAdmin: (adminId: string) => void; + + //is the admin in editing mode? + // TODO: remove - legacy feature editing: boolean; } diff --git a/src/client/pages/AdminsPage/index.tsx b/src/client/pages/AdminsPage/index.tsx index db6b392d..afcd9319 100644 --- a/src/client/pages/AdminsPage/index.tsx +++ b/src/client/pages/AdminsPage/index.tsx @@ -24,17 +24,35 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface AdminsPageProps { } +/** + * This component receives props in 3 ways - + * 1) The explicit props provied to it by AdminsPageProps + * 2) The redux state provided to it by mapStateToProps + * 3) The dispatch functions provided to it by mapDispatchToProps + * + * So, the props of this component is the union of the return types of mapStateToProps, + * mapDispatchToProps and AdminsPageProps + */ type Props = ReturnType & ReturnType & AdminsPageProps; interface AdminsPageState { + + //tracks whether the New Admin Modal is open or not isRegisterModalOpen: boolean; } +/** + * This pageshows a list of all admins in the system. It also can create and delete admins. + * This page is locked to users with the Developer role (The logic for this is in AdminLayout). + */ class AdminsPage extends React.Component { state: Readonly = { isRegisterModalOpen: false, }; + /** + * Get admins from the backend and put them into the redux state. + */ loadAdmins = () => loadAllAdmins() .then(res => this.props.replaceAdmins(res)) @@ -43,14 +61,22 @@ class AdminsPage extends React.Component { componentDidMount() { this.props.showLoading(); + //Hide loading state after the API returns the admins this.loadAdmins() .then(() => this.props.hideLoading()); } + /** + * Toggle the isRegisterModalOpen state variable to show or hide new admin modal + */ toggleRegisterModal = () => this.setState({ isRegisterModalOpen: !this.state.isRegisterModalOpen, }); + /** + * Create a new admin in the database and update the frontend to reflect the change + * @param {NewAdminModalFormData} newAdmin the new admin to be added to the system + */ registerNewAdmin = (newAdmin: NewAdminModalFormData) => { registerAdmin(newAdmin) .then(this.loadAdmins) @@ -58,6 +84,10 @@ class AdminsPage extends React.Component { .catch(console.error); } + /** + * Delete an admin from the system, and update the frontend to reflect the change + * @param {String} adminId the admin to be deleted from the system + */ onDeleteAdmin = (adminId: string) => deleteAdmin(adminId) .then(this.loadAdmins) diff --git a/src/client/pages/AdminsPage/reducers/Admins.ts b/src/client/pages/AdminsPage/reducers/Admins.ts index e86d7639..6cf69dcd 100644 --- a/src/client/pages/AdminsPage/reducers/Admins.ts +++ b/src/client/pages/AdminsPage/reducers/Admins.ts @@ -7,6 +7,7 @@ import * as Types from '../actions/types'; const initialState: Admin[] = []; +//Tell redux what to do when it sees ADD_ADMINS and REPLACE_ADMINS export default handleActions({ [Types.ADD_ADMINS]: (state, action: ActionType) => ([ ...state, diff --git a/src/client/pages/AlertPage.tsx b/src/client/pages/AlertPage.tsx index 1b85094f..f2347792 100644 --- a/src/client/pages/AlertPage.tsx +++ b/src/client/pages/AlertPage.tsx @@ -35,6 +35,7 @@ export const AlertPageAbove: React.StatelessComponent = (props) => { * Allows for extension with bootstrap alerts in the state. */ export default class AlertPage extends React.Component { + /** * Creates a new alert to render to the top of the screen. * @param {String} message The message to display in the alert. @@ -52,7 +53,7 @@ export default class AlertPage extends React.Compon } /** - * Creates a new error alert if there was a login error. + * Creates a new error alert if there was an error. * @param {PageAlert} alert The alert to display. * @param {String} key The given key for the element map. * @param {Boolean} container Determines whether the alert is wrapped in a container. @@ -75,12 +76,21 @@ export default class AlertPage extends React.Compon } } + /** + * Empties the alerts in the state. + */ clearAlerts = () => { this.setState({ alerts: [], }); }; + /** + * Show the alerts currently in state. + * @param {Boolean} container Determines whether the alert is wrapped in a container. + * @param {className} Override the alert with a different className. + * @returns {Component} + */ renderAlerts = (container: boolean = false, className: string = 'alert-page__alert') => { return ( diff --git a/src/client/pages/ApplyPage/components/ApplyPageSection.tsx b/src/client/pages/ApplyPage/components/ApplyPageSection.tsx index f2ecf52f..d5c15a4c 100644 --- a/src/client/pages/ApplyPage/components/ApplyPageSection.tsx +++ b/src/client/pages/ApplyPage/components/ApplyPageSection.tsx @@ -3,10 +3,19 @@ import React from 'react'; import { InjectedFormProps } from 'redux-form'; export interface ApplyPageSectionProps { + + //The event this application is for event: TESCEvent; + + //function to be called when the back button is pressed goToPreviousPage?: () => void; } +/** + * This is an abstraction that creates a React component with the props for + * the current event being applied to and a function that makes the ApplyPage + * move the page. Each section of the application simply extends this class. + */ export default class ApplyPageSection extends React.Component

, S> { diff --git a/src/client/pages/ApplyPage/components/Header.tsx b/src/client/pages/ApplyPage/components/Header.tsx index 6d7c418b..c31a7586 100644 --- a/src/client/pages/ApplyPage/components/Header.tsx +++ b/src/client/pages/ApplyPage/components/Header.tsx @@ -2,8 +2,14 @@ import { Logo } from '@Shared/ModelTypes'; import React from 'react'; interface HeaderProps { + + //Name of the event name: string; + + //Logo of the event logo: Logo; + + //Description of the event description: string; } diff --git a/src/client/pages/ApplyPage/components/PersonalSection.tsx b/src/client/pages/ApplyPage/components/PersonalSection.tsx index aeeb5ef3..6e4b56b7 100644 --- a/src/client/pages/ApplyPage/components/PersonalSection.tsx +++ b/src/client/pages/ApplyPage/components/PersonalSection.tsx @@ -9,7 +9,11 @@ import ApplyPageSection, { ApplyPageSectionProps } from './ApplyPageSection'; import UniversityField from './UniversityField'; interface PersonalSectionProps extends ApplyPageSectionProps { + + //function to be called when the email changes, to check if the user exists in the database onEmailChange: (newEmail: string) => void; + + //the current event being applied to event: TESCEvent; } @@ -19,6 +23,9 @@ export enum InstitutionType { HighSchool = 'hs', } +/** + * Override defined form data to track the data in the way the UI shows it. + */ export interface PersonalSectionFormData extends RegisterUserPersonalSectionRequest { birthdateMonth: number; birthdateDay: number; @@ -28,6 +35,13 @@ export interface PersonalSectionFormData extends RegisterUserPersonalSectionRequ } class PersonalSection extends ApplyPageSection { + + + /** + * Create the email component of the application. + * Use onBlur to check if the email exists when the user takes their focus off this field. + * @returns {Component} + */ createEmailField() { return ( ; } - // TODO: Make into a statically-typed method + /** + * Render the custom questions for this event, given the type of question to be rendered + * TODO: Make into a statically-typed method + * @param {CustomQuestions} customQuestions the custom questions of this event + * @param {QuestionType} type The type of question to be rendered + */ renderCustomQuestions(customQuestions: CustomQuestions, type: QuestionType) { let inputField: (fieldName: string, value: any, ...otherArgs: any[]) => JSX.Element | JSX.Element[] = null; diff --git a/src/client/pages/ApplyPage/components/UniversityField.tsx b/src/client/pages/ApplyPage/components/UniversityField.tsx index 2e2ed6f0..ec0fecd1 100644 --- a/src/client/pages/ApplyPage/components/UniversityField.tsx +++ b/src/client/pages/ApplyPage/components/UniversityField.tsx @@ -12,6 +12,12 @@ interface UniversityFieldProps { type Props = WrappedFieldProps & UniversityFieldProps; export default class UniversityField extends React.Component { + + /** + * Function that is called when the user selects a suggestion from the AutoSuggest + * TODO: better documentation + * @param {String} suggestion The suggestion that the user selected + */ onUniversitySelected = (suggestion: string) => { const {input} = this.props; input.onChange(suggestion); diff --git a/src/client/pages/ApplyPage/index.tsx b/src/client/pages/ApplyPage/index.tsx index 18488f4e..3aeb5b07 100644 --- a/src/client/pages/ApplyPage/index.tsx +++ b/src/client/pages/ApplyPage/index.tsx @@ -19,19 +19,34 @@ interface ApplyPageProps { } type Props = ApplyPageProps & RouteComponentProps<{ + //eventAlias for the event this application is for eventAlias: string; }>; interface ApplyPageState { + + //tracks which application page the user is on page: number; + + //tracks application error error: Error; + + //tracks if the user has yet to submit the application isSubmitting: boolean; + + //the event this application is for event: TESCEvent; + + //does the user have an exisiting tesc.events account? emailExists: boolean; } +//The complete application is the union of data from its 3 pages export type ApplyPageFormData = PersonalSectionFormData & ResponseSectionFormData & UserSectionFormData; +/** + * This page is the application for an event. It implements a multi page form. + */ class ApplyPage extends React.Component { state: Readonly = { page: 1, @@ -88,7 +103,13 @@ class ApplyPage extends React.Component { this.loadPageFromHash(); } + /** + * Sanitise user input, used before the final submit. + * @param {ApplyPageFormData} values the user's application + */ sanitiseValues(values: ApplyPageFormData) { + + //parse date numbers into a datestring values.birthdate = new Date( values.birthdateYear, values.birthdateMonth - 1, @@ -130,6 +151,7 @@ class ApplyPage extends React.Component { return; } + //checks if user exists with an API call checkUserExists(email) .then((ret) => { this.setState({ @@ -152,6 +174,7 @@ class ApplyPage extends React.Component { isSubmitting: true, }); + //Send Application to backend registerUser(this.props.match.params.eventAlias, values) .then(() => { // Log successful application with Google Analytics @@ -160,6 +183,7 @@ class ApplyPage extends React.Component { action: 'Successful', }); + //Show success page this.nextPage(); }) .catch((err) => { diff --git a/src/client/pages/README.md b/src/client/pages/README.md index 2c3375c2..d0b77308 100644 --- a/src/client/pages/README.md +++ b/src/client/pages/README.md @@ -2,6 +2,7 @@ An `XYZPage` in this directory implements a page in the application. Any `XYZPage` can define redux actions in `XYZPage/actions`, reducers in `XYZPage/reducers` or components it uses in `XYZPage/components`. +# Directory Tree ``` ├── AdminsPage * [Admin with role Developer only] It shows a list of all admins in the system. From 2fc4eb2269fe7e92ccfe0958f6908668401ac6a4 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 18 May 2019 21:39:12 -0700 Subject: [PATCH 20/31] DashboardPage finished, TODO SettingsTab + StatisticsTab --- .../components/AdminDashboard.tsx | 4 +++ .../DashboardPage/components/EventList.tsx | 6 ++++ .../components/SponsorDashboard.tsx | 2 ++ src/client/pages/DashboardPage/index.tsx | 15 ++++++++ src/client/pages/EventPage/index.tsx | 17 +++++++++ .../pages/EventPage/tabs/ActionsTab.tsx | 35 ++++++++++--------- .../EventPage/tabs/AdministratorsTab.tsx | 13 +++++++ .../pages/EventPage/tabs/EventPageTab.tsx | 4 +++ 8 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/client/pages/DashboardPage/components/AdminDashboard.tsx b/src/client/pages/DashboardPage/components/AdminDashboard.tsx index 00487143..20f18aa5 100644 --- a/src/client/pages/DashboardPage/components/AdminDashboard.tsx +++ b/src/client/pages/DashboardPage/components/AdminDashboard.tsx @@ -6,7 +6,11 @@ import React from 'react'; import EventList from './EventList'; interface AdminDashboardProps { + + //events that the admin is permitted to see events: TESCEvent[]; + + //The current user (aka admin) requesting the page user: JWTAdminAuthToken; } diff --git a/src/client/pages/DashboardPage/components/EventList.tsx b/src/client/pages/DashboardPage/components/EventList.tsx index f098b2a1..a052cbba 100644 --- a/src/client/pages/DashboardPage/components/EventList.tsx +++ b/src/client/pages/DashboardPage/components/EventList.tsx @@ -5,8 +5,14 @@ import { Link } from 'react-router-dom'; import EventCard from '~/components/EventCard'; interface EventListProps { + + //events to be rendered in the list events: TESCEvent[]; + + //track if we need a direct link to the resume page resumeLink?: boolean; + + //can this user create an event? canCreate?: boolean; } diff --git a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx index d14db74e..f62a5957 100644 --- a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx +++ b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx @@ -4,6 +4,8 @@ import React from 'react'; import EventList from './EventList'; interface SponsorDashboardProps { + + //events that this sponsor is allowed to see events: TESCEvent[]; } diff --git a/src/client/pages/DashboardPage/index.tsx b/src/client/pages/DashboardPage/index.tsx index 46169aa6..42d0049b 100644 --- a/src/client/pages/DashboardPage/index.tsx +++ b/src/client/pages/DashboardPage/index.tsx @@ -24,12 +24,27 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface DashboardPageProps { } +/** + * This component receives props in 3 ways - + * 1) The explicit props provied to it by DashboardPageProps + * 2) The redux state provided to it by mapStateToProps + * 3) The dispatch functions provided to it by mapDispatchToProps + * + * So, the props of this component is the union of the return types of mapStateToProps, + * mapDispatchToProps and DashboardPageProps + */ type Props = ReturnType & ReturnType & DashboardPageProps; +/** + * This is the page that an admin sees when they first log into tesc.events. + * It shows the list of events that admin is permitted to see + */ class DashboardPage extends React.Component { + componentDidMount() { this.props.showLoading(); + //hide the loading state at the end of this promise this.props.loadAllAdminEvents() .catch(console.error) .finally(this.props.hideLoading); diff --git a/src/client/pages/EventPage/index.tsx b/src/client/pages/EventPage/index.tsx index a7c64c65..ff8c9e91 100644 --- a/src/client/pages/EventPage/index.tsx +++ b/src/client/pages/EventPage/index.tsx @@ -29,6 +29,8 @@ import EventForm, { EventFormData } from '../../components/EventForm'; import createValidator from '../NewEventPage/validate'; type RouteProps = RouteComponentProps<{ + + //the eventAlias for the event we want to render the dashboard for eventAlias: string; }>; @@ -59,6 +61,15 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface EventPageProps extends TabularPageProps { } +/** + * This component receives props in 3 ways - + * 1) The explicit props provied to it by EventPageProps + * 2) The redux state provided to it by mapStateToProps + * 3) The dispatch functions provided to it by mapDispatchToProps + * + * So, the props of this component is the union of the return types of mapStateToProps, + * mapDispatchToProps and EventPageProps + */ export type Props = RouteProps & ReturnType & ReturnType & EventPageProps; @@ -66,6 +77,12 @@ interface EventPageState extends TabularPageState { teams: TESCTeam[]; } +/** + * This page renders the main page for an event. + * It has tabs and links to the other pages related to this event. + * + * This component is extending from TabularPage, which has the tabbing functionality abstracted away + */ class EventPage extends TabularPage { tabPages: Readonly = [ { diff --git a/src/client/pages/EventPage/tabs/ActionsTab.tsx b/src/client/pages/EventPage/tabs/ActionsTab.tsx index b1887184..50018aa3 100644 --- a/src/client/pages/EventPage/tabs/ActionsTab.tsx +++ b/src/client/pages/EventPage/tabs/ActionsTab.tsx @@ -8,26 +8,24 @@ import EventPageTab from './EventPageTab'; interface ActionsTabProps { } +/** + * ActionsTab holds the functionality for actions related to the event + * This tab currently has: + * - Export All Users As CSV + * - Bulk Status Change of multiple users by user IDs + */ export default class ActionsTab extends EventPageTab { + + /** + * This function is called when the user clicks the 'Export All Users' button. + */ exportUsers = () => { const eventAlias = this.props.event.alias; - exportUsers(eventAlias, false) - .end((err, res) => { - // Download as file - const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.setAttribute('download', `${eventAlias}-${Date.now()}.csv`); - document.body.appendChild(link); - - link.click(); - }); - } - exportEmails = () => { - const eventAlias = this.props.event.alias; - exportUsers(eventAlias, true) + //call API's exportUsers. + //surprisingly, this is okay syntax because exportUsers and this.exportUsers are different + //TODO: name API call better + exportUsers(eventAlias, false) .end((err, res) => { // Download as file const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' }); @@ -41,6 +39,10 @@ export default class ActionsTab extends EventPageTab { }); } + /** + * This function is called when the Bulk Change button is clicked on the form. + * @param {BulkChangeFormData} values the values of the Bulk Change Form + */ onBulkChange = (values: BulkChangeFormData) => { const { event } = this.props; const { users, status } = values; @@ -48,6 +50,7 @@ export default class ActionsTab extends EventPageTab { // Split users into array const usersSplit = users.split(/\n/); + //call API's bulk change bulkChange(usersSplit, status) .then(() => { this.props.addEventSuccessAlert(event.alias, 'Successfully updated users!', 'Bulk Change'); diff --git a/src/client/pages/EventPage/tabs/AdministratorsTab.tsx b/src/client/pages/EventPage/tabs/AdministratorsTab.tsx index 24c4a5a9..d64b82f8 100644 --- a/src/client/pages/EventPage/tabs/AdministratorsTab.tsx +++ b/src/client/pages/EventPage/tabs/AdministratorsTab.tsx @@ -12,10 +12,23 @@ interface AdministratorsTabProps { } interface AdminReference { + + //the database ID of an admin _id: string; + + //the username of an admin username: string; } +/** + * View Administrators in this event + * + * This tab currently has: + * - View current organizers + * - View current sponsors + * - Create a new sponsor + * - Create a new organizer + */ export default class AdministratorsTab extends EventPageTab { /** * Parses from a react-select element into an admin object. diff --git a/src/client/pages/EventPage/tabs/EventPageTab.tsx b/src/client/pages/EventPage/tabs/EventPageTab.tsx index 926504d3..92f8ba90 100644 --- a/src/client/pages/EventPage/tabs/EventPageTab.tsx +++ b/src/client/pages/EventPage/tabs/EventPageTab.tsx @@ -7,5 +7,9 @@ interface EventPageTabProps { event: TESCEvent; } +/** + * This is an abstraction that provides every EventPage tab with access to the event directly. + * This means that we don't have to explicitly tell every tab which event we are currently on. + */ export default class EventPageTab extends React.Component

{ } From 4da75a54d1830f46541cdb9ed445b091c5d2aae4 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 15:00:58 -0700 Subject: [PATCH 21/31] Working through DashboardPage/components --- .../pages/AdminsPage/components/AdminList.tsx | 3 ++ .../pages/ApplyPage/components/Header.tsx | 3 ++ .../ApplyPage/components/PersonalSection.tsx | 4 +- .../ApplyPage/components/ResponseSection.tsx | 3 ++ .../ApplyPage/components/SubmittedSection.tsx | 3 ++ .../ApplyPage/components/UniversityField.tsx | 3 ++ .../ApplyPage/components/UserSection.tsx | 3 ++ src/client/pages/ApplyPage/index.tsx | 2 +- .../components/AdminDashboard.tsx | 3 ++ .../DashboardPage/components/EventList.tsx | 4 ++ .../components/SponsorDashboard.tsx | 4 ++ .../pages/EventPage/components/BulkChange.tsx | 8 ++++ .../components/CheckinStatistics.tsx | 3 ++ .../EventPage/components/CustomQuestion.tsx | 10 +++++ .../pages/EventPage/tabs/SettingsTab.tsx | 37 ++++++++++++++++++- .../pages/EventPage/tabs/StatisticsTab.tsx | 6 +++ 16 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/client/pages/AdminsPage/components/AdminList.tsx b/src/client/pages/AdminsPage/components/AdminList.tsx index 61df617b..ff6d54cd 100644 --- a/src/client/pages/AdminsPage/components/AdminList.tsx +++ b/src/client/pages/AdminsPage/components/AdminList.tsx @@ -26,6 +26,9 @@ interface AdminListState { columns: DisplayColumnMap; } +/** + * This component renders a list of admins on the admins page + */ export default class AdminList extends React.Component { state: Readonly = { columns: { diff --git a/src/client/pages/ApplyPage/components/Header.tsx b/src/client/pages/ApplyPage/components/Header.tsx index c31a7586..9c068656 100644 --- a/src/client/pages/ApplyPage/components/Header.tsx +++ b/src/client/pages/ApplyPage/components/Header.tsx @@ -13,6 +13,9 @@ interface HeaderProps { description: string; } +/** + * This is the header for the application. It shows the event name, description and logo. + */ export default class Header extends React.Component { render() { const {name, logo, description} = this.props; diff --git a/src/client/pages/ApplyPage/components/PersonalSection.tsx b/src/client/pages/ApplyPage/components/PersonalSection.tsx index 6e4b56b7..4d7a7b39 100644 --- a/src/client/pages/ApplyPage/components/PersonalSection.tsx +++ b/src/client/pages/ApplyPage/components/PersonalSection.tsx @@ -34,9 +34,11 @@ export interface PersonalSectionFormData extends RegisterUserPersonalSectionRequ resume?: File[]; } +/** + * This is the first page of the event application. This handles the user's personal details. + */ class PersonalSection extends ApplyPageSection { - /** * Create the email component of the application. * Use onBlur to check if the email exists when the user takes their focus off this field. diff --git a/src/client/pages/ApplyPage/components/ResponseSection.tsx b/src/client/pages/ApplyPage/components/ResponseSection.tsx index 3847f2b3..39d9b103 100644 --- a/src/client/pages/ApplyPage/components/ResponseSection.tsx +++ b/src/client/pages/ApplyPage/components/ResponseSection.tsx @@ -21,6 +21,9 @@ export interface ResponseSectionFormData extends RegisterUserResponseSectionRequ city?: string; } +/** + * This is the second page of the application. It handles the user's response to event-specific questions + */ class ResponseSection extends ApplyPageSection { state: Readonly = { teamState: undefined, diff --git a/src/client/pages/ApplyPage/components/SubmittedSection.tsx b/src/client/pages/ApplyPage/components/SubmittedSection.tsx index 6e3d8618..5bef7274 100644 --- a/src/client/pages/ApplyPage/components/SubmittedSection.tsx +++ b/src/client/pages/ApplyPage/components/SubmittedSection.tsx @@ -6,6 +6,9 @@ import ApplyPageSection, { ApplyPageSectionProps } from './ApplyPageSection'; interface SubmittedSectionProps extends ApplyPageSectionProps { } +/** + * This is the application success page + */ class SubmittedSection extends ApplyPageSection<{}, SubmittedSectionProps> { renderTeamCode = (info: WrappedFieldProps) => (

diff --git a/src/client/pages/ApplyPage/components/UniversityField.tsx b/src/client/pages/ApplyPage/components/UniversityField.tsx index ec0fecd1..c6cf3e85 100644 --- a/src/client/pages/ApplyPage/components/UniversityField.tsx +++ b/src/client/pages/ApplyPage/components/UniversityField.tsx @@ -11,6 +11,9 @@ interface UniversityFieldProps { type Props = WrappedFieldProps & UniversityFieldProps; +/** + * This component creates the university picker. It is used in ./PersonalSection.tsx + */ export default class UniversityField extends React.Component { /** diff --git a/src/client/pages/ApplyPage/components/UserSection.tsx b/src/client/pages/ApplyPage/components/UserSection.tsx index 0c8d75de..239379e7 100644 --- a/src/client/pages/ApplyPage/components/UserSection.tsx +++ b/src/client/pages/ApplyPage/components/UserSection.tsx @@ -18,6 +18,9 @@ export interface UserSectionFormData { confirmPassword?: string; } +/** + * This is the 3rd page of the application. It handles tesc.events account creation and MLH provisions + */ class UserSection extends ApplyPageSection { /** * Create a checkbox to accept the Code of Conduct. diff --git a/src/client/pages/ApplyPage/index.tsx b/src/client/pages/ApplyPage/index.tsx index 3aeb5b07..e60ff3f9 100644 --- a/src/client/pages/ApplyPage/index.tsx +++ b/src/client/pages/ApplyPage/index.tsx @@ -45,7 +45,7 @@ interface ApplyPageState { export type ApplyPageFormData = PersonalSectionFormData & ResponseSectionFormData & UserSectionFormData; /** - * This page is the application for an event. It implements a multi page form. + * This page is the application for an event. It implements a multi page application form. */ class ApplyPage extends React.Component { state: Readonly = { diff --git a/src/client/pages/DashboardPage/components/AdminDashboard.tsx b/src/client/pages/DashboardPage/components/AdminDashboard.tsx index 20f18aa5..fbb8fc6c 100644 --- a/src/client/pages/DashboardPage/components/AdminDashboard.tsx +++ b/src/client/pages/DashboardPage/components/AdminDashboard.tsx @@ -14,6 +14,9 @@ interface AdminDashboardProps { user: JWTAdminAuthToken; } +/** + * This is the admin dashboard. It is primarily a wrapper around EventList for now. + */ export default class AdminDashboard extends React.Component { render() { diff --git a/src/client/pages/DashboardPage/components/EventList.tsx b/src/client/pages/DashboardPage/components/EventList.tsx index a052cbba..cb255369 100644 --- a/src/client/pages/DashboardPage/components/EventList.tsx +++ b/src/client/pages/DashboardPage/components/EventList.tsx @@ -16,6 +16,10 @@ interface EventListProps { canCreate?: boolean; } +/** + * This component renders the cards for events on DashboardPage. + * It also has an "Add Event" button if the user is capable of user creation. + */ export default class EventList extends React.Component { render() { const { events, resumeLink, canCreate } = this.props; diff --git a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx index f62a5957..0a44a886 100644 --- a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx +++ b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx @@ -9,6 +9,10 @@ interface SponsorDashboardProps { events: TESCEvent[]; } +/** + * This is the sponsor's event list. It uses EventList's resumeLink prop to + * enforce a link to the sponsor portal on clicking the card. + */ export default class SponsorDashboard extends React.Component { render() { const {events} = this.props; diff --git a/src/client/pages/EventPage/components/BulkChange.tsx b/src/client/pages/EventPage/components/BulkChange.tsx index aefa7b4d..6579a7c6 100644 --- a/src/client/pages/EventPage/components/BulkChange.tsx +++ b/src/client/pages/EventPage/components/BulkChange.tsx @@ -3,7 +3,11 @@ import React from 'react'; import { Field, reduxForm, InjectedFormProps } from 'redux-form'; export interface BulkChangeFormData { + + // new line separated users: string; + + //new status to be set status: string; } @@ -11,8 +15,12 @@ interface BulkChangeProps { } +//Use a union of BulkChangeProps and Redux Form's Props type Props = InjectedFormProps & BulkChangeProps; +/** + * This component implements a feature to update multiple users' statuses by their user IDs + */ class BulkChange extends React.Component { render() { const {handleSubmit, pristine, submitting} = this.props; diff --git a/src/client/pages/EventPage/components/CheckinStatistics.tsx b/src/client/pages/EventPage/components/CheckinStatistics.tsx index 22f6ac55..daacfc79 100644 --- a/src/client/pages/EventPage/components/CheckinStatistics.tsx +++ b/src/client/pages/EventPage/components/CheckinStatistics.tsx @@ -3,6 +3,9 @@ import { Link } from 'react-router-dom'; import EventStatisticsComponent from './EventStatisticsComponent'; +/** + * This component shows the number of checkedin users in the header of the page + */ export default class CheckinStatistics extends EventStatisticsComponent { render() { const {event, statistics} = this.props; diff --git a/src/client/pages/EventPage/components/CustomQuestion.tsx b/src/client/pages/EventPage/components/CustomQuestion.tsx index d4ac013f..3b9ced6e 100644 --- a/src/client/pages/EventPage/components/CustomQuestion.tsx +++ b/src/client/pages/EventPage/components/CustomQuestion.tsx @@ -4,11 +4,21 @@ import FA from 'react-fontawesome'; import ToggleSwitch from '~/components/ToggleSwitch'; interface CustomQuestionProps { + + //the question this component describes question: Question; + + //callback for the delete question button onDelete: () => void; + + //callback for the questions' required switch toggle onChangeRequired: (newRequired: boolean) => void; } +/** + * This component renders a custom question on ./SettingsTab.tsx + * It handles the functionality for toggling the required field, and deleting the question. + */ class CustomQuestion extends React.Component { render() { diff --git a/src/client/pages/EventPage/tabs/SettingsTab.tsx b/src/client/pages/EventPage/tabs/SettingsTab.tsx index 0c185d69..cdcf4265 100644 --- a/src/client/pages/EventPage/tabs/SettingsTab.tsx +++ b/src/client/pages/EventPage/tabs/SettingsTab.tsx @@ -14,10 +14,19 @@ import EventPageTab from './EventPageTab'; interface SettingsTabProps { } +//TODO: not sure why this exists as opposed to a boolean? interface SettingsTabState { customQuestionsRequests: number; } +/** + * This is the settings tag for an event. This tab currently has: + * + * - Toggling event options + * - Add custom question + * - Update custom question + * - Delete custom question + */ export default class SettingsTab extends EventPageTab { state: Readonly = { customQuestionsRequests: 0, @@ -25,7 +34,7 @@ export default class SettingsTab extends EventPageTab { const { event, addEventSuccessAlert, addEventDangerAlert } = this.props; @@ -41,18 +50,31 @@ export default class SettingsTab extends EventPageTab { this.setState({ customQuestionsRequests: this.state.customQuestionsRequests + 1, }); }; + /** + * Set React state to hide loader after server response has been seen. + */ stopCustomQuestionsLoading = () => { this.setState({ customQuestionsRequests: this.state.customQuestionsRequests - 1, }); }; + /** + * Handles the CustomQuestions callback for when custom questions should be added. + * + * @param {QuestionType} type The type of question being added + * @param {Question} question The new question to send to the server. + */ onAddCustomQuestion = (type: QuestionType, question: Question) => { const { event, loadAllAdminEvents, addEventDangerAlert } = this.props; @@ -67,6 +89,13 @@ export default class SettingsTab extends EventPageTab { const { event, loadAllAdminEvents, addEventDangerAlert } = this.props; @@ -81,6 +110,12 @@ export default class SettingsTab extends EventPageTab { const { event, loadAllAdminEvents, addEventDangerAlert } = this.props; diff --git a/src/client/pages/EventPage/tabs/StatisticsTab.tsx b/src/client/pages/EventPage/tabs/StatisticsTab.tsx index 3a695c60..53077e09 100644 --- a/src/client/pages/EventPage/tabs/StatisticsTab.tsx +++ b/src/client/pages/EventPage/tabs/StatisticsTab.tsx @@ -12,6 +12,12 @@ interface StatisticsTabProps { statistics: EventStatistics | null; } +/** + * This is the tab that shows the user statistics for an event. This tab currently has: + * + * - Status Breakdown Piechart + * - Gender Breakdown Piechart + */ export default class StatisticsTab extends EventPageTab { render() { From 5c157b2ce58f9ebf870470c46aca377a4b4474cb Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 16:33:16 -0700 Subject: [PATCH 22/31] Event, HomePage Documentation complete --- .../EventPage/components/EventOptionsEdit.tsx | 22 ++++++++++++++ .../components/EventStatisticsCharts.tsx | 9 ++++++ .../components/EventStatisticsComponent.tsx | 3 ++ .../EventPage/components/GenderStatistics.tsx | 9 ++++++ .../EventPage/components/OrganiserList.tsx | 30 +++++++++++++++++++ .../EventPage/components/QuestionInput.tsx | 8 +++++ .../EventPage/components/ResumeStatistics.tsx | 3 ++ .../EventPage/components/SponsorList.tsx | 29 ++++++++++++++++++ .../HomePage/components/CurrentEvents.tsx | 3 ++ .../pages/HomePage/components/UserEvents.tsx | 5 ++++ src/client/pages/HomePage/index.tsx | 17 +++++++++++ 11 files changed, 138 insertions(+) diff --git a/src/client/pages/EventPage/components/EventOptionsEdit.tsx b/src/client/pages/EventPage/components/EventOptionsEdit.tsx index 627c0d9b..fa95ab4b 100644 --- a/src/client/pages/EventPage/components/EventOptionsEdit.tsx +++ b/src/client/pages/EventPage/components/EventOptionsEdit.tsx @@ -5,15 +5,24 @@ import { UncontrolledTooltip } from 'reactstrap'; import ToggleSwitch from '~/components/ToggleSwitch'; interface EventOptionsProps { + //initial options of the event options: TESCEventOptions; + + //calback function for when the update button is clicked onOptionsUpdate: (newOptions: TESCEventOptions) => void; + + //the event these options are for event: TESCEvent; } +//the current, edited state of the options interface EventOptionsState { options: TESCEventOptions; } +/** + * This component implements event option editing (toggling) + */ export default class EventOptionsEdit extends React.Component { constructor(props: EventOptionsProps) { super(props); @@ -23,6 +32,11 @@ export default class EventOptionsEdit extends React.Component () => { this.setState({ // TODO: Fix dynamically property @@ -31,6 +45,12 @@ export default class EventOptionsEdit extends React.Component ( @@ -44,6 +64,7 @@ export default class EventOptionsEdit extends React.ComponentUnique Universities, diff --git a/src/client/pages/EventPage/components/EventStatisticsComponent.tsx b/src/client/pages/EventPage/components/EventStatisticsComponent.tsx index f6f284dd..e2ece7b3 100644 --- a/src/client/pages/EventPage/components/EventStatisticsComponent.tsx +++ b/src/client/pages/EventPage/components/EventStatisticsComponent.tsx @@ -7,6 +7,9 @@ interface EventStatisticsComponentProps { statistics: EventStatistics | null; } +/** + * This is an abstraction that is used by statistics components to provide an event and statistics prop to them. + */ export default class EventStatisticsComponent

extends React.Component

{ diff --git a/src/client/pages/EventPage/components/GenderStatistics.tsx b/src/client/pages/EventPage/components/GenderStatistics.tsx index f04959a4..cec75d20 100644 --- a/src/client/pages/EventPage/components/GenderStatistics.tsx +++ b/src/client/pages/EventPage/components/GenderStatistics.tsx @@ -7,7 +7,16 @@ import EventStatisticsComponent from './EventStatisticsComponent'; const PIE_CHART_COLOURS = ['#8E44AD', '#43D2F0', '#AEF9D6', '#EF767A', '#7D7ABC']; +/** + * This component renders a statistics pie chart for the gender breakdown of an event + */ export default class GenderStatistics extends EventStatisticsComponent { + + /** + * Render the gender breakdown in text form + * + * @param {EventStatistics} statistics The statistics of the event + */ renderStats(statistics: EventStatistics) { return [

Gender Distribution
, diff --git a/src/client/pages/EventPage/components/OrganiserList.tsx b/src/client/pages/EventPage/components/OrganiserList.tsx index 9b7281b0..e4c56af8 100644 --- a/src/client/pages/EventPage/components/OrganiserList.tsx +++ b/src/client/pages/EventPage/components/OrganiserList.tsx @@ -7,27 +7,49 @@ import NewAdminModal, { NewAdminModalFormData } from '~/components/NewAdminModal import OrganiserSelect from '~/components/OrganiserSelect'; interface OrganiserListProps { + + //list of organisers for the event organisers: Admin[]; + + //callback function to show a modal to add a new organiser to the event addNewOrganiser: (toAdd: AdminSelectType) => void; + + //callback to add the organiser to the event registerNewOrganiser: (newOrganiser: NewAdminModalFormData) => void; } interface OrganiserListState { + + //the new organiser to be added to the event newOrganiser: AdminSelectType; + + //boolean to track if the new organiser modal is open or not isRegisterModalOpen: boolean; } +/** + * This component renders an organiser list on the administrators tab of an event + */ export default class OrganiserList extends React.Component { state: Readonly = { newOrganiser: null, isRegisterModalOpen: false, }; + + /** + * Update the components newOrganiser state to the new data + * + * @param {AdminSelectType} newOrganiser the new organiser to be set + */ changeNewOrganiser = (newOrganiser: AdminSelectType) => this.setState({ newOrganiser, }); + /** + * Add an (existing organiser) to the system from the dropdown + */ onAddNewOrganiser = () => { const {newOrganiser} = this.state; @@ -36,12 +58,20 @@ export default class OrganiserList extends React.Component { this.props.registerNewOrganiser(values); this.toggleRegisterModal(); }; + /** + * Toggle the react state to show the modal or not. + */ toggleRegisterModal = () => this.setState({ isRegisterModalOpen: !this.state.isRegisterModalOpen, }); diff --git a/src/client/pages/EventPage/components/QuestionInput.tsx b/src/client/pages/EventPage/components/QuestionInput.tsx index e43c2d5c..680988fe 100644 --- a/src/client/pages/EventPage/components/QuestionInput.tsx +++ b/src/client/pages/EventPage/components/QuestionInput.tsx @@ -12,14 +12,22 @@ interface QuestionInputState { isRequired: boolean; } +/** + * This component creates an input field to create a custom question. + */ export default class QuestionInput extends React.Component { state: Readonly = { question: '', isRequired: false, }; + /** + * Callback function called when the add button is pressed to add this question to the system + */ addQuestion = () => { this.props.onAddQuestion(this.state); + + //reset the component state this.setState({ question: '', isRequired: false, diff --git a/src/client/pages/EventPage/components/ResumeStatistics.tsx b/src/client/pages/EventPage/components/ResumeStatistics.tsx index f81d9480..f74c680b 100644 --- a/src/client/pages/EventPage/components/ResumeStatistics.tsx +++ b/src/client/pages/EventPage/components/ResumeStatistics.tsx @@ -9,6 +9,9 @@ interface ResumeStatisticsProps { className: string; } +/** + * Event header link to show the number of resumes in the system + */ export default class ResumeStatistics extends EventStatisticsComponent { /** * Renders the tooltip that explains how to approve resumes. diff --git a/src/client/pages/EventPage/components/SponsorList.tsx b/src/client/pages/EventPage/components/SponsorList.tsx index d7794b27..9e94556a 100644 --- a/src/client/pages/EventPage/components/SponsorList.tsx +++ b/src/client/pages/EventPage/components/SponsorList.tsx @@ -7,27 +7,48 @@ import NewAdminModal, { NewAdminModalFormData } from '~/components/NewAdminModal import SponsorSelect from '~/components/SponsorSelect'; interface SponsorListProps { + + //the sponsors for this event sponsors: Admin[]; + + //callback function to add an existing sponsor to the event addNewSponsor: (toAdd: AdminSelectType) => void; + + //function called to create a new sponsor in the system registerNewSponsor: (newSponsor: NewAdminModalFormData) => void; } interface SponsorListState { + + //the new sponsor to be added newSponsor: AdminSelectType; + + //boolean to track if the create sponsor modal is open or not. isRegisterModalOpen: boolean; } +/** + * This component renders a sponsor list on the administrators tab of an event + */ export default class SponsorList extends React.Component { state: Readonly = { newSponsor: null, isRegisterModalOpen: false, }; + /** + * Update the components newSponsor state to the new data + * + * @param {AdminSelectType} newOrganiser the new sponsor to be set + */ changeNewSponsor = (newSponsor: AdminSelectType) => this.setState({ newSponsor, }); + /** + * Add an (existing sponsor) to the system from the dropdown + */ onAddNewSponsor = () => { const {newSponsor} = this.state; @@ -36,12 +57,20 @@ export default class SponsorList extends React.Component { this.props.registerNewSponsor(values); this.toggleRegisterModal(); }; + /** + * Toggle the react state to show the modal or not. + */ toggleRegisterModal = () => this.setState({ isRegisterModalOpen: !this.state.isRegisterModalOpen, }); diff --git a/src/client/pages/HomePage/components/CurrentEvents.tsx b/src/client/pages/HomePage/components/CurrentEvents.tsx index 647232a7..66e1d109 100644 --- a/src/client/pages/HomePage/components/CurrentEvents.tsx +++ b/src/client/pages/HomePage/components/CurrentEvents.tsx @@ -6,6 +6,9 @@ interface CurrentEventProps { events: TESCEvent[]; } +/** + * This component shows all upcoming events with open applications + */ export default class CurrentEvents extends React.Component { render() { const { events } = this.props; diff --git a/src/client/pages/HomePage/components/UserEvents.tsx b/src/client/pages/HomePage/components/UserEvents.tsx index 5819f021..6c1f8e1a 100644 --- a/src/client/pages/HomePage/components/UserEvents.tsx +++ b/src/client/pages/HomePage/components/UserEvents.tsx @@ -3,9 +3,14 @@ import React from 'react'; import { Link } from 'react-router-dom'; interface UserEventsProps { + + //the events that the user has applied to events: TESCEvent[]; } +/** + * This component shows all existing applications for the user. + */ export default class UserEvents extends React.Component { render() { const { events } = this.props; diff --git a/src/client/pages/HomePage/index.tsx b/src/client/pages/HomePage/index.tsx index 5d2eece1..23642159 100644 --- a/src/client/pages/HomePage/index.tsx +++ b/src/client/pages/HomePage/index.tsx @@ -31,7 +31,12 @@ interface HomePageProps { type Props = ReturnType & ReturnType & HomePageProps; +/** + * This is the main tesc.event homepage. If the user requesting it is authenticated, it will show + * the user their existing applications. If the user is not authenticated, it will show all open applications + */ class HomePage extends React.Component { + componentDidMount() { this.props.showLoading(); @@ -53,6 +58,11 @@ class HomePage extends React.Component { return true; } + /** + * Renders the user's existing applications + * + * @param {TESCEvent[]} events the events that this user has applied to + */ userEvents(events: TESCEvent[]) { return (
@@ -61,6 +71,12 @@ class HomePage extends React.Component { ); } + /** + * Renders the current events that are open for applications + * + * @param {TESCEvent[]} events the events to be rendered + * @param {Boolean} small display mode for the event cards + */ currentEvents(events: TESCEvent[], small: boolean = false) { return (
@@ -72,6 +88,7 @@ class HomePage extends React.Component { render() { const { events, userEvents } = this.props; + //show the sidebar if the authenticated user has any applications const showSidebar = Object.values(userEvents).length > 0; let currentEvents: TESCEvent[] = []; From be514e04f31d7d457d5f9ab03b2fadde9ce3e23e Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 16:42:30 -0700 Subject: [PATCH 23/31] NewEventPage Documentation Complete --- src/client/components/EventForm.tsx | 14 ++++++++++++++ src/client/pages/NewEventPage/index.tsx | 10 ++++++++++ src/client/pages/NewEventPage/validate.ts | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/client/components/EventForm.tsx b/src/client/components/EventForm.tsx index 9ee7c86e..420820a3 100644 --- a/src/client/components/EventForm.tsx +++ b/src/client/components/EventForm.tsx @@ -21,10 +21,19 @@ interface EventFormProps { editing?: boolean; } +//the props of this component are the props returned by the redux-form HOC and it's native props type Props = InjectedFormProps & EventFormProps; +/** + * This is the redux-form to create a new event + */ class EventForm extends React.Component { + /** + * Create a file droppable field for the event logo + * + * @returns {Component} + */ createLogoUpload() { return ( { ); } + /** + * Show an alert that flags the event as a non-TESC hosted event. + * + * @returns {React.StatelessComponent} + */ showThirdPartyText: React.StatelessComponent = ({values}) => { if (values && values.organisedBy && values.organisedBy.input.value !== 'TESC') { return ( diff --git a/src/client/pages/NewEventPage/index.tsx b/src/client/pages/NewEventPage/index.tsx index 261496c6..da8ad79e 100644 --- a/src/client/pages/NewEventPage/index.tsx +++ b/src/client/pages/NewEventPage/index.tsx @@ -28,12 +28,22 @@ interface NewEventPageState { err: Error; } +/** + * This page holds the form to create a new event in the system + */ class NewEventPage extends React.Component { state: Readonly = { err: null, }; + /** + * Create a new event in the system + * + * @param {NewEventFormData} event the event to be created + */ createNewEvent = (event: EventFormData) => { + + //send event to API registerNewEvent(event) .then((res: TESCEvent) => { this.setState({ err: null }); diff --git a/src/client/pages/NewEventPage/validate.ts b/src/client/pages/NewEventPage/validate.ts index d6a025ce..667272db 100644 --- a/src/client/pages/NewEventPage/validate.ts +++ b/src/client/pages/NewEventPage/validate.ts @@ -1,5 +1,7 @@ +/** + * New Event Form validator (Redux Form) + */ const createValidator = (requireLogo = true, allowPastDates = false) => (values: any) => { - console.log(values) const errors: any = {}; const required = ['name', 'alias', 'closeTimeMonth', 'closeTimeDay', From c18ed86bf2ccdc07d29ab08473cf5bc8dad177e9 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 17:02:53 -0700 Subject: [PATCH 24/31] TODO RSVPConfirm onwards and UsersPage --- .../ResumesPage/components/ResumeList.tsx | 11 +++++++++++ src/client/pages/ResumesPage/index.tsx | 18 +++++++++++++++--- .../pages/UserPage/components/BussingModal.tsx | 15 +++++++++++++++ .../pages/UserPage/components/RSVPConfirm.tsx | 3 +++ src/client/pages/UserPage/index.tsx | 10 +++++++++- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/client/pages/ResumesPage/components/ResumeList.tsx b/src/client/pages/ResumesPage/components/ResumeList.tsx index dad98060..0bd57c09 100644 --- a/src/client/pages/ResumesPage/components/ResumeList.tsx +++ b/src/client/pages/ResumesPage/components/ResumeList.tsx @@ -3,8 +3,14 @@ import React from 'react'; import ToggleSwitch from '~/components/ToggleSwitch'; interface ResumeListProps { + + //callback for when the compact state toggle is clicked onCompactChange: () => void; + + //the compacted state isCompacted: boolean; + + //the users to render applicants: TESCUser[]; } @@ -13,11 +19,16 @@ interface ColumnMap { } interface ResumeListState { + + //columns shown on screen columns: ColumnMap; smallColumns: string[]; mediumColumns: string[]; } +/** + * This is the resume list that is rendered in the sponsor tool + */ class ResumeList extends React.Component { state: Readonly = { columns: { diff --git a/src/client/pages/ResumesPage/index.tsx b/src/client/pages/ResumesPage/index.tsx index b14e2c0e..dcb77739 100644 --- a/src/client/pages/ResumesPage/index.tsx +++ b/src/client/pages/ResumesPage/index.tsx @@ -1,4 +1,4 @@ -import { TESCUser } from '@Shared/ModelTypes'; + import { TESCUser } from '@Shared/ModelTypes'; import { UserStatus } from '@Shared/UserStatus'; import React from 'react'; import { connect } from 'react-redux'; @@ -38,18 +38,30 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface ResumesPageProps { } -type Props = RouteProps & ReturnType & ReturnType & ResumesPageProps; +// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// regular props of the component +type Props = RouteComponentProps<{ + eventAlias: string; +}> & ReturnType & ReturnType & ResumesPageProps; interface ResumesPageState { + + //boolean to track compact state isCompacted: boolean; } +/** + * This is the sponsor tool that shows a list of applicants to an event and their resumes + */ class ResumesPage extends React.Component { state: Readonly = { isCompacted: false, }; - toggleCompacted = () => this.setState({ isCompacted: !this.state.isCompacted }); + /** + * Toggle to compact react state + */ + toggleCompacted = () => this.setState({isCompacted: !this.state.isCompacted}); componentDidMount() { const { showLoading, hideLoading } = this.props; diff --git a/src/client/pages/UserPage/components/BussingModal.tsx b/src/client/pages/UserPage/components/BussingModal.tsx index 19eabf63..53fdd2c2 100644 --- a/src/client/pages/UserPage/components/BussingModal.tsx +++ b/src/client/pages/UserPage/components/BussingModal.tsx @@ -2,12 +2,27 @@ import React from 'react'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface BussingModalProps { + + //boolean to track if the modal is open isOpen: boolean; + + //is a bus available for this school? availableBus?: string; + + //callback for when the bus is clicked onChooseBus: (choice: boolean) => void; } +/** + * This is the modal that a user can use to show that they will be getting a bus. + */ export default class BussingModal extends React.Component { + + /** + * Callback for the bus choose button + * + * @param {Boolean} bussing boolean to track bussing status + */ onChooseBus = (bussing: boolean) => () => this.props.onChooseBus(bussing); diff --git a/src/client/pages/UserPage/components/RSVPConfirm.tsx b/src/client/pages/UserPage/components/RSVPConfirm.tsx index d0c5a0e3..8dfdfd38 100644 --- a/src/client/pages/UserPage/components/RSVPConfirm.tsx +++ b/src/client/pages/UserPage/components/RSVPConfirm.tsx @@ -16,6 +16,9 @@ interface RSVPConfirmState { status: boolean; } +/** + * + */ export default class RSVPConfirm extends React.Component { state: Readonly = { page: 0, diff --git a/src/client/pages/UserPage/index.tsx b/src/client/pages/UserPage/index.tsx index 69fa0be5..bd5e2129 100644 --- a/src/client/pages/UserPage/index.tsx +++ b/src/client/pages/UserPage/index.tsx @@ -33,6 +33,8 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface UserPageProps { } +// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// regular props of the component type Props = RouteComponentProps<{ eventAlias: string; }> & ReturnType & ReturnType & UserPageProps; @@ -41,6 +43,9 @@ interface UserPageState extends AlertPageState { showRSVP: boolean; } +/** + * This is the page that shows a user their application + */ class UserPage extends AlertPage { state: Readonly = { alerts: [], @@ -91,7 +96,10 @@ class UserPage extends AlertPage { }); } - toggleRSVP = () => this.setState({ showRSVP: !this.state.showRSVP }); + /** + * Toggle React state to show the RSVP modal + */ + toggleRSVP = () => this.setState({showRSVP: !this.state.showRSVP}); /** * Requests that the server RSVP the current user with the given values. From c7b4585ef29f589a0ece63500041d2dc69c1e95b Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 25 May 2019 17:32:32 -0700 Subject: [PATCH 25/31] User and UsersPage done --- .../pages/UserPage/components/RSVPConfirm.tsx | 8 +++- .../pages/UserPage/components/RSVPModal.tsx | 13 ++++++ .../pages/UserPage/components/UserProfile.tsx | 46 ++++++++++++++++++- .../UsersPage/components/ColumnEditor.tsx | 2 +- .../pages/UsersPage/components/UserList.tsx | 12 +++++ src/client/pages/UsersPage/index.tsx | 3 ++ 6 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/client/pages/UserPage/components/RSVPConfirm.tsx b/src/client/pages/UserPage/components/RSVPConfirm.tsx index 8dfdfd38..ba9057ca 100644 --- a/src/client/pages/UserPage/components/RSVPConfirm.tsx +++ b/src/client/pages/UserPage/components/RSVPConfirm.tsx @@ -17,7 +17,7 @@ interface RSVPConfirmState { } /** - * + * This is the component that renders the RSVP workflow by showing RSVPModal and BussingModal */ export default class RSVPConfirm extends React.Component { state: Readonly = { @@ -25,6 +25,7 @@ export default class RSVPConfirm extends React.Component this.setState({page: this.state.page + 1}); onChooseStatus = (status: boolean) => { @@ -45,6 +46,11 @@ export default class RSVPConfirm extends React.Component { const {onUpdate, onClose} = this.props; diff --git a/src/client/pages/UserPage/components/RSVPModal.tsx b/src/client/pages/UserPage/components/RSVPModal.tsx index c1e48a21..602f53a7 100644 --- a/src/client/pages/UserPage/components/RSVPModal.tsx +++ b/src/client/pages/UserPage/components/RSVPModal.tsx @@ -3,14 +3,27 @@ import React from 'react'; import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface RSVPModalProps { + + //toggle function for the model toggle: () => void; + + //variable to track open or close state of the modal isOpen: boolean; + //callback function for when the status is chosen onChooseStatus: (statusChoice: boolean) => void; + + //the event for which the current application is on event: TESCEvent; } export default class RSVPModal extends React.Component { + + /** + * Callback for when an RSVP status is chosen + * + * @param {Boolean} status RSVP true or false + */ onChooseStatus = (status: boolean) => () => this.props.onChooseStatus(status); render() { diff --git a/src/client/pages/UserPage/components/UserProfile.tsx b/src/client/pages/UserPage/components/UserProfile.tsx index bde8da5f..c7109488 100644 --- a/src/client/pages/UserPage/components/UserProfile.tsx +++ b/src/client/pages/UserPage/components/UserProfile.tsx @@ -7,6 +7,7 @@ import { CustomFieldProps } from '~/components/Fields'; import FileField from '~/components/FileField'; import { generateQRCodeURL } from '@Shared/QRCodes'; import FA from 'react-fontawesome'; +import { JSXElement } from 'babel-types'; export interface UserProfileFormData { gender: string; @@ -23,14 +24,31 @@ export interface UserProfileFormData { } interface UserProfileProps { + + //the user for which the profile is rendered user: TESCUser; + + //the event for which the application is on event: TESCEvent; + + //callback function to toggle RSVP status toggleRSVP: () => void; } type Props = InjectedFormProps & UserProfileProps; +/** + * This is the component that shows the user their data on the application page + * + * It also provides functionality to edit their application + */ class UserProfile extends React.Component { + /** + * Render the gender selection. + * + * @param {Object} _ an object with an input and className field + * @returns {React.StatelessComponent} + */ genderSelect: React.StatelessComponent = ({ input, className }) => { return ( ); }; - + + /** + * Render the T-Shirt size selection. + * + * @param {Object} _ an object with an input and className field + * @returns {React.StatelessComponent} + */ pronounSelect: React.StatelessComponent = ({ input, className }) => { return ( ); }; - + /** * Render the T-Shirt size selection. - * + * * @param {Object} _ an object with an input and className field * @returns {React.StatelessComponent} */ @@ -188,9 +188,9 @@ class UserProfile extends React.Component { /** * Render the phone number and santitize it - * + * * @param {String} phone the phone number string - * @returns {String} + * @returns {String} */ renderPhoneNumber = (phone: string) => ( phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3') @@ -198,7 +198,7 @@ class UserProfile extends React.Component { /** * Render the Applicant's info - * + * * @param {TESCUser} user the info of the applicant * @returns {JSXElement} */ @@ -267,7 +267,7 @@ class UserProfile extends React.Component { /** * Render a user's preference section. - * + * * @param {TESCUser} user the user's data * @returns {JSXElement} */ diff --git a/src/client/pages/UserPage/index.tsx b/src/client/pages/UserPage/index.tsx index bd5e2129..c215f767 100644 --- a/src/client/pages/UserPage/index.tsx +++ b/src/client/pages/UserPage/index.tsx @@ -33,7 +33,7 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface UserPageProps { } -// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// the props of this event are the union of the react-router data, redux actions and dispatch, and the // regular props of the component type Props = RouteComponentProps<{ eventAlias: string; diff --git a/src/client/pages/UsersPage/components/ColumnEditor.tsx b/src/client/pages/UsersPage/components/ColumnEditor.tsx index 71260659..5ed97aa2 100644 --- a/src/client/pages/UsersPage/components/ColumnEditor.tsx +++ b/src/client/pages/UsersPage/components/ColumnEditor.tsx @@ -83,4 +83,4 @@ export default class UserList extends React.Component ); } -} \ No newline at end of file +} diff --git a/src/client/pages/UsersPage/components/UserList.tsx b/src/client/pages/UsersPage/components/UserList.tsx index 13e147bb..28d78fae 100644 --- a/src/client/pages/UsersPage/components/UserList.tsx +++ b/src/client/pages/UsersPage/components/UserList.tsx @@ -11,7 +11,7 @@ const styles = require('react-table/react-table.css'); interface UserListProps { - //users that have applied to an event + // users that have applied to an event users: TESCUser[]; columns: AutofillColumn[]; event: TESCEvent; @@ -26,7 +26,7 @@ class UserList extends React.Component { /** * Render the User component, used by react-table's SubComponent - * + * * @param {TESCUser} row the user to be rendered * @returns {JSXElement} */ @@ -49,7 +49,7 @@ class UserList extends React.Component { data={this.props.users} column={{ ...ReactTableDefaults.column, - Cell: ({value}) => value ? String(value): value + Cell: ({value}) => value ? String(value): value, }} columns={this.props.columns} defaultPageSize={10} diff --git a/src/client/pages/UsersPage/index.tsx b/src/client/pages/UsersPage/index.tsx index e6612f3a..69902e50 100644 --- a/src/client/pages/UsersPage/index.tsx +++ b/src/client/pages/UsersPage/index.tsx @@ -44,7 +44,7 @@ type RouteProps = RouteComponentProps<{ eventAlias: string; }>; -//the props of this page is the union of the react-router, redux and explicit props +// the props of this page is the union of the react-router, redux and explicit props type Props = RouteProps & ReturnType & ReturnType & UsersPageProps; interface UsersPageState extends AlertPageState { @@ -132,7 +132,7 @@ class UsersPage extends AlertPage { /** * Handles an update to a user in the list. - * + * * @param {TESCUser} user the user to be updated */ onUserUpdate = (user: TESCUser) => { diff --git a/src/client/routes.tsx b/src/client/routes.tsx index 91ff0028..2f197391 100644 --- a/src/client/routes.tsx +++ b/src/client/routes.tsx @@ -13,34 +13,24 @@ import { authorised as AdminAuthorised } from '~/data/AdminApi'; import { authorised as UserAuthorised } from '~/data/UserApi'; import CookieTypes from '~/static/Cookies'; -/* - PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around - react-router-dom's Route component to handle authentication state. -*/ import PrivateRoute from './PrivateRoute'; import PrivateUserRoute from './PrivateUserRoute'; - -//Authentication Components & Actions -//TODO: Document better import { ApplicationDispatch } from './actions'; import AdminLogout from './auth/admin/Logout'; import { finishAuthorisation, authoriseAdmin, logoutAdmin } from './auth/admin/actions'; import ConfirmPage from './auth/user/Confirm'; import UserLogout from './auth/user/Logout'; import { authoriseUser, finishAuthorisation as finishUserAuth, logoutUser } from './auth/user/actions'; - -//Importing the different layouts (page structures) for the application import AdminLayout from './layouts/admin'; -import SponsorLayout from './layouts/sponsor'; import PublicLayout from './layouts/public'; +import SponsorLayout from './layouts/sponsor'; import UserLayout from './layouts/user'; - -//Importing all the pages for the app, used later when setting up routes. import AdminsPage from './pages/AdminsPage'; import ApplyPage from './pages/ApplyPage'; import CheckinPage from './pages/CheckinPage'; import Dashboard from './pages/DashboardPage'; import EventPage from './pages/EventPage'; +import PreviewApplication from './pages/EventPage/components/PreviewApplication'; import ForgotPage from './pages/ForgotPage'; import HomePage from './pages/HomePage'; import LoginPage from './pages/LoginPage'; @@ -50,7 +40,18 @@ import ResetPage from './pages/ResetPage'; import ResumesPage from './pages/ResumesPage'; import UserPage from './pages/UserPage'; import UsersPage from './pages/UsersPage'; -import PreviewApplication from './pages/EventPage/components/PreviewApplication'; + +/* + PrivateRoute.tsx and PrivateUserRoute.tsx are wrapper components around + react-router-dom's Route component to handle authentication state. +*/ + +// Authentication Components & Actions +// TODO: Document better + +// Importing the different layouts (page structures) for the application + +// Importing all the pages for the app, used later when setting up routes. const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators({ authoriseAdmin, @@ -167,7 +168,7 @@ class Routes extends React.Component { } renderPublic = (RenderComponent: any) => { - return (props: RouteComponentProps) => + return (props: RouteComponentProps) => ( diff --git a/src/shared/QRCodes.ts b/src/shared/QRCodes.ts index 74e80741..40f62f53 100644 --- a/src/shared/QRCodes.ts +++ b/src/shared/QRCodes.ts @@ -1,10 +1,10 @@ import { TESCUser } from '@Shared/ModelTypes'; const QR_CODE_SIZE = 200; -const QR_CODE_API_ROOT = `https://api.qrserver.com/v1/create-qr-code/` +const QR_CODE_API_ROOT = `https://api.qrserver.com/v1/create-qr-code/`; function generateQRCodeURL(user: TESCUser) { - return `${QR_CODE_API_ROOT}?size=${QR_CODE_SIZE}x${QR_CODE_SIZE}&data=${user._id}` + return `${QR_CODE_API_ROOT}?size=${QR_CODE_SIZE}x${QR_CODE_SIZE}&data=${user._id}`; } -export { generateQRCodeURL } \ No newline at end of file +export { generateQRCodeURL }; diff --git a/src/shared/api/Requests.ts b/src/shared/api/Requests.ts index 060774e7..4ba587ef 100644 --- a/src/shared/api/Requests.ts +++ b/src/shared/api/Requests.ts @@ -142,4 +142,4 @@ export interface RSVPUserRequest { export interface StatusEmailRequest { user: TESCUser; -} \ No newline at end of file +} diff --git a/src/shared/api/Responses.ts b/src/shared/api/Responses.ts index dd897ca9..2bdac334 100644 --- a/src/shared/api/Responses.ts +++ b/src/shared/api/Responses.ts @@ -14,7 +14,7 @@ export interface EventStatistics { resumes: number; appsOverTime: { [Date: string]: number; - } + }; } export type EventUserCounts = { From 8337ffcc4773baabf6572275df4d25c45fb54c02 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 8 Aug 2020 16:30:03 -0400 Subject: [PATCH 28/31] Ran tslint --fix --- src/client/components/EventForm.tsx | 2 +- src/client/components/Fields.tsx | 6 +++++- src/client/components/Hero.tsx | 1 - src/client/pages/UsersPage/index.tsx | 2 +- tslint.json | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/client/components/EventForm.tsx b/src/client/components/EventForm.tsx index 810812a2..81b840a5 100644 --- a/src/client/components/EventForm.tsx +++ b/src/client/components/EventForm.tsx @@ -21,7 +21,7 @@ interface EventFormProps { editing?: boolean; } -// the props of this component are the props returned by the redux-form HOC and it's native props +// The props of this component are the props returned by the redux-form HOC and it's native props type Props = InjectedFormProps & EventFormProps; /** diff --git a/src/client/components/Fields.tsx b/src/client/components/Fields.tsx index 9512f590..07bddf5c 100644 --- a/src/client/components/Fields.tsx +++ b/src/client/components/Fields.tsx @@ -1,4 +1,8 @@ -import { UserDiversityOptions, UserYearOptions, UserGenderOptions, UserPronounOptions, UserShirtSizeOptions } from '@Shared/UserEnums'; +import { UserDiversityOptions, + UserYearOptions, + UserGenderOptions, + UserPronounOptions, + UserShirtSizeOptions } from '@Shared/UserEnums'; import React from 'react'; import { Field, WrappedFieldProps } from 'redux-form'; import majors from '~/static/Majors.json'; diff --git a/src/client/components/Hero.tsx b/src/client/components/Hero.tsx index e30f8de9..b67039a3 100644 --- a/src/client/components/Hero.tsx +++ b/src/client/components/Hero.tsx @@ -22,7 +22,6 @@ export default class Hero extends React.Component { } return ( -
); } diff --git a/src/client/pages/UsersPage/index.tsx b/src/client/pages/UsersPage/index.tsx index 69902e50..22a33e5f 100644 --- a/src/client/pages/UsersPage/index.tsx +++ b/src/client/pages/UsersPage/index.tsx @@ -44,7 +44,7 @@ type RouteProps = RouteComponentProps<{ eventAlias: string; }>; -// the props of this page is the union of the react-router, redux and explicit props +// The props of this page is the union of the react-router, redux and explicit props type Props = RouteProps & ReturnType & ReturnType & UsersPageProps; interface UsersPageState extends AlertPageState { diff --git a/tslint.json b/tslint.json index 1be00e29..848e24ed 100644 --- a/tslint.json +++ b/tslint.json @@ -15,7 +15,7 @@ ], "comment-format": [ true, - "check-space" + "check-space", ], "forin": false, "import-blacklist": [ From b4ca73687aae5f8b220f4e9317aad79706fa7955 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 29 Aug 2020 20:08:02 -0400 Subject: [PATCH 29/31] Last pass for frontend docs --- src/client/PrivateUserRoute.tsx | 8 ++++ .../pages/AdminsPage/components/AdminList.tsx | 6 +-- src/client/pages/AdminsPage/index.tsx | 2 +- .../ApplyPage/components/PersonalSection.tsx | 16 ++++++-- src/client/pages/ApplyPage/index.tsx | 10 ++--- src/client/pages/CheckinPage/actions/index.ts | 8 ++++ .../components/KeyboardScanner.tsx | 9 ++++ .../components/LiabilityWaiverModal.tsx | 11 +++++ .../CheckinPage/components/ManualScanner.tsx | 16 ++++++++ .../pages/CheckinPage/components/Scanner.tsx | 3 ++ .../CheckinPage/components/WebcamScanner.tsx | 9 ++++ src/client/pages/CheckinPage/index.tsx | 41 +++++++++++++++++++ .../pages/CheckinPage/reducers/Checkin.ts | 4 ++ .../components/AdminDashboard.tsx | 2 +- .../components/SponsorDashboard.tsx | 2 +- src/client/pages/DashboardPage/index.tsx | 2 +- .../pages/EventPage/components/BulkChange.tsx | 4 +- .../EventPage/components/CustomQuestion.tsx | 6 +-- .../components/CustomQuestionsEdit.tsx | 21 ++++++++++ .../EventPage/components/EventOptionsEdit.tsx | 8 ++-- .../EventPage/components/OrganiserList.tsx | 10 ++--- .../components/PreviewApplication.tsx | 3 ++ .../EventPage/components/SponsorList.tsx | 10 ++--- src/client/pages/EventPage/index.tsx | 2 +- .../pages/HomePage/components/UserEvents.tsx | 2 +- src/client/pages/LoginPage.tsx | 8 ++++ .../ResumesPage/components/ResumeList.tsx | 8 ++-- src/client/pages/ResumesPage/index.tsx | 4 +- .../UserPage/components/BussingModal.tsx | 6 +-- .../pages/UserPage/components/RSVPModal.tsx | 8 ++-- .../pages/UserPage/components/UserProfile.tsx | 6 +-- src/client/pages/UserPage/index.tsx | 2 +- 32 files changed, 203 insertions(+), 54 deletions(-) diff --git a/src/client/PrivateUserRoute.tsx b/src/client/PrivateUserRoute.tsx index 5d9d38ed..5cc9f52f 100644 --- a/src/client/PrivateUserRoute.tsx +++ b/src/client/PrivateUserRoute.tsx @@ -5,7 +5,12 @@ import { Route, Redirect, RouteProps } from 'react-router-dom'; import { ApplicationState } from './reducers'; interface StateProps { + + // Is the current user authenticated. authenticated: boolean; + + // Is there an authentication process in progress - this is used to prevent + // race conditions. authFinished: boolean; } @@ -16,6 +21,9 @@ interface PrivateUserRouteProps { type Props = RouteProps & StateProps & PrivateUserRouteProps; +/** + * Lock down a view by authentication state. + */ class PrivateUserRoute extends React.Component { render() { return ( diff --git a/src/client/pages/AdminsPage/components/AdminList.tsx b/src/client/pages/AdminsPage/components/AdminList.tsx index 6734fe3d..e8a39b9e 100644 --- a/src/client/pages/AdminsPage/components/AdminList.tsx +++ b/src/client/pages/AdminsPage/components/AdminList.tsx @@ -5,13 +5,13 @@ import { Button } from 'reactstrap'; interface AdminListProps { - // the admins in the system + // The admins in the system admins: Admin[]; - // function to be called when the delete button is pressed + // Function to be called when the delete button is pressed onDeleteAdmin: (adminId: string) => void; - // is the admin in editing mode? + // Is the admin in editing mode // TODO: remove - legacy feature editing: boolean; } diff --git a/src/client/pages/AdminsPage/index.tsx b/src/client/pages/AdminsPage/index.tsx index cf2281c0..c36a76c0 100644 --- a/src/client/pages/AdminsPage/index.tsx +++ b/src/client/pages/AdminsPage/index.tsx @@ -37,7 +37,7 @@ type Props = ReturnType & ReturnType void; - - // the current event being applied to + // The current event being applied to event: TESCEvent; } @@ -191,6 +189,11 @@ class PersonalSection extends ApplyPageSection> => (dispatch: ApplicationDispatch) => Api.checkinUser(user._id, eventId) @@ -13,4 +18,7 @@ export const userCheckin = (user: TESCUser, eventId: string): ApplicationAction< }) .catch(console.error); +/** + * Redux action to to be dispatched when a user in checked in. + */ export const _userCheckin = createStandardAction(Types.CHECKIN_USER)(); diff --git a/src/client/pages/CheckinPage/components/KeyboardScanner.tsx b/src/client/pages/CheckinPage/components/KeyboardScanner.tsx index c2d84dd2..e5e0ab81 100644 --- a/src/client/pages/CheckinPage/components/KeyboardScanner.tsx +++ b/src/client/pages/CheckinPage/components/KeyboardScanner.tsx @@ -8,11 +8,20 @@ interface KeyboardScannerState { currentCode: string; } +/** + * React Component to render an input where an organizer can scan in a user ID + * through a QR scanner. + */ export default class KeyboardScanner extends Scanner<{}, KeyboardScannerState> { state: Readonly = { currentCode: '', }; + /** + * Callback to be made after the user ID is scanned. + * + * @param {React.FormEvent} event the input event being responded to. + */ onChangeInput = (event: React.FormEvent) => { const newValue = event.currentTarget.value; diff --git a/src/client/pages/CheckinPage/components/LiabilityWaiverModal.tsx b/src/client/pages/CheckinPage/components/LiabilityWaiverModal.tsx index 9b20cc27..89182e7b 100644 --- a/src/client/pages/CheckinPage/components/LiabilityWaiverModal.tsx +++ b/src/client/pages/CheckinPage/components/LiabilityWaiverModal.tsx @@ -3,12 +3,23 @@ import React from 'react'; import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; interface LiabilityWaiverModalProps { + + // The event for which we want to show the liability waiver. event: TESCEvent; + + // Callback to be called after agreement to the waiver onWaiverAgree: () => void; + + // Callback to toggle this modal toggleModal: () => void; + + // The state of this modal. isOpen: boolean; } +/** + * The Liability waiver that a user must agree to to be let into the event. + */ export default class LiabilityWaiverModal extends React.Component { render() { const {event, isOpen, toggleModal, onWaiverAgree} = this.props; diff --git a/src/client/pages/CheckinPage/components/ManualScanner.tsx b/src/client/pages/CheckinPage/components/ManualScanner.tsx index 500726dc..c06ae6ee 100644 --- a/src/client/pages/CheckinPage/components/ManualScanner.tsx +++ b/src/client/pages/CheckinPage/components/ManualScanner.tsx @@ -4,10 +4,13 @@ import React from 'react'; import Scanner from './Scanner'; interface ManualScannerProps { + + // All users in this event. users: TESCUser[]; } interface ManualScannerState { + // All eligible users for the search query. filteredApplicants: TESCUser[]; } @@ -16,10 +19,17 @@ export default class ManualScanner extends Scanner) => { const {users} = this.props; const name = event.currentTarget.value; + + // Do not issue a search for less than 3 charactes. if (name.length < 3) { return; } @@ -33,6 +43,12 @@ export default class ManualScanner extends Scanner { this.setState({ filteredApplicants: [], diff --git a/src/client/pages/CheckinPage/components/Scanner.tsx b/src/client/pages/CheckinPage/components/Scanner.tsx index ae6df33a..e630ac87 100644 --- a/src/client/pages/CheckinPage/components/Scanner.tsx +++ b/src/client/pages/CheckinPage/components/Scanner.tsx @@ -4,6 +4,9 @@ interface ScannerProps { onUserScanned: (userId: string) => void; } +/** + * Abstraction to ensure the `onUserScanned` prop is provided to the scanner tabs. + */ export default class Scanner

extends React.Component

{ } diff --git a/src/client/pages/CheckinPage/components/WebcamScanner.tsx b/src/client/pages/CheckinPage/components/WebcamScanner.tsx index db09ccc1..a9a2ecd4 100644 --- a/src/client/pages/CheckinPage/components/WebcamScanner.tsx +++ b/src/client/pages/CheckinPage/components/WebcamScanner.tsx @@ -5,7 +5,16 @@ import { USER_ID_LENGTH } from '..'; import Scanner from './Scanner'; +/** + * Renders a QR Code Scanner and reads the user ID. + */ export default class WebcamScanner extends Scanner { + + /** + * The callback to be called with the scanned data from the QR code. + * + * @param {String} data The data scanned from the QR code. + */ onScan = (data: string) => { if (data === null || data.length !== USER_ID_LENGTH) { return; diff --git a/src/client/pages/CheckinPage/index.tsx b/src/client/pages/CheckinPage/index.tsx index ac51b0b4..ddd60f56 100644 --- a/src/client/pages/CheckinPage/index.tsx +++ b/src/client/pages/CheckinPage/index.tsx @@ -108,11 +108,19 @@ class CheckinPage extends TabularPage { } } + /** + * Show error when checkin fails + * + * @param {String} error the error message to show. + */ onCheckinError = (error: string) => { this.clearAlerts(); this.createAlert(error, AlertType.Danger); }; + /** + * Show success message when checkin is successful + */ onCheckinSuccessful = () => { const { lastUser } = this.state; @@ -131,6 +139,11 @@ class CheckinPage extends TabularPage { .then(addUsers); } + /** + * Logic flow to determine whether the user should be allowed into the event. + * + * @param {TESCUser} user The user being checked. + */ validateUser = (user: TESCUser) => new Promise((resolve, reject) => { // Ensure they're eligible @@ -154,6 +167,11 @@ class CheckinPage extends TabularPage { return resolve(user); }) + /** + * Make network request to check in the user. + * + * @param {TESCUser} user the user to be checked in. + */ checkinUser = (user: TESCUser): Promise => new Promise((resolve, reject) => { const { event } = this.props; @@ -166,6 +184,11 @@ class CheckinPage extends TabularPage { .catch(reject); }); + /** + * Callback to prep the component for validating the user. + * + * @param {String} userId the userId scanned from the QR code. + */ onScan = (userId: string) => { const { lastUser } = this.state; @@ -196,11 +219,17 @@ class CheckinPage extends TabularPage { this.toggleModal(); } + /** + * Toggle liability waiver modal visibility state. + */ toggleModal = () => this.setState({ isLiabilityShowing: !this.state.isLiabilityShowing, }); + /** + * Higher level flow for the checkin process. + */ callCheckin = () => { this.setState({ isLiabilityShowing: false, @@ -221,6 +250,10 @@ class CheckinPage extends TabularPage { }); } + /** + * Render tab where the user can use a scanner to scan in a user id from a QR code. + * @param props {Props} Not used in this function. + */ renderKeyboardTab(props: Props) { return ( @@ -229,6 +262,10 @@ class CheckinPage extends TabularPage { ); } + /** + * Render tab where the application can scan a QR code. + * @param props {Props} Not used in this function. + */ renderWebcamTab(props: Props) { return ( @@ -237,6 +274,10 @@ class CheckinPage extends TabularPage { ); } + /** + * Render tab where the user can type in an email / name to check in a user. + * @param props {Props} Not used in this function. + */ renderManualTab() { return ( diff --git a/src/client/pages/CheckinPage/reducers/Checkin.ts b/src/client/pages/CheckinPage/reducers/Checkin.ts index 50019037..f2f812dc 100644 --- a/src/client/pages/CheckinPage/reducers/Checkin.ts +++ b/src/client/pages/CheckinPage/reducers/Checkin.ts @@ -8,6 +8,10 @@ import * as Types from '../actions/types'; const initialState: TESCUser[] = []; +/** + * Redux dispatch listener for when a user is checked in to reflect the change in + * Redux state. + */ export default handleActions({ [Types.CHECKIN_USER]: (state, action: ActionType) => ([ ...state.filter(x => x._id !== action.payload._id), { diff --git a/src/client/pages/DashboardPage/components/AdminDashboard.tsx b/src/client/pages/DashboardPage/components/AdminDashboard.tsx index 1e9609fd..e9ffc6a0 100644 --- a/src/client/pages/DashboardPage/components/AdminDashboard.tsx +++ b/src/client/pages/DashboardPage/components/AdminDashboard.tsx @@ -7,7 +7,7 @@ import EventList from './EventList'; interface AdminDashboardProps { - // events that the admin is permitted to see + // Events that the admin is permitted to see events: TESCEvent[]; // The current user (aka admin) requesting the page diff --git a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx index 052e6d5b..8ec31242 100644 --- a/src/client/pages/DashboardPage/components/SponsorDashboard.tsx +++ b/src/client/pages/DashboardPage/components/SponsorDashboard.tsx @@ -5,7 +5,7 @@ import EventList from './EventList'; interface SponsorDashboardProps { - // events that this sponsor is allowed to see + // Events that this sponsor is allowed to see events: TESCEvent[]; } diff --git a/src/client/pages/DashboardPage/index.tsx b/src/client/pages/DashboardPage/index.tsx index e48f2bf1..8f584d93 100644 --- a/src/client/pages/DashboardPage/index.tsx +++ b/src/client/pages/DashboardPage/index.tsx @@ -44,7 +44,7 @@ class DashboardPage extends React.Component { componentDidMount() { this.props.showLoading(); - // hide the loading state at the end of this promise + // Hide the loading state at the end of this promise this.props.loadAllAdminEvents() .catch(console.error) .finally(this.props.hideLoading); diff --git a/src/client/pages/EventPage/components/BulkChange.tsx b/src/client/pages/EventPage/components/BulkChange.tsx index 68e208be..c5a23782 100644 --- a/src/client/pages/EventPage/components/BulkChange.tsx +++ b/src/client/pages/EventPage/components/BulkChange.tsx @@ -4,10 +4,10 @@ import { Field, reduxForm, InjectedFormProps } from 'redux-form'; export interface BulkChangeFormData { - // new line separated + // New line separated users: string; - // new status to be set + // New status to be set status: string; } diff --git a/src/client/pages/EventPage/components/CustomQuestion.tsx b/src/client/pages/EventPage/components/CustomQuestion.tsx index 57f62f63..6a8ad299 100644 --- a/src/client/pages/EventPage/components/CustomQuestion.tsx +++ b/src/client/pages/EventPage/components/CustomQuestion.tsx @@ -5,13 +5,13 @@ import ToggleSwitch from '~/components/ToggleSwitch'; interface CustomQuestionProps { - // the question this component describes + // The question this component describes question: Question; - // callback for the delete question button + // Callback for the delete question button onDelete: () => void; - // callback for the questions' required switch toggle + // Callback for the questions' required switch toggle onChangeRequired: (newRequired: boolean) => void; } diff --git a/src/client/pages/EventPage/components/CustomQuestionsEdit.tsx b/src/client/pages/EventPage/components/CustomQuestionsEdit.tsx index c8a98ca5..3091a4ac 100644 --- a/src/client/pages/EventPage/components/CustomQuestionsEdit.tsx +++ b/src/client/pages/EventPage/components/CustomQuestionsEdit.tsx @@ -6,23 +6,44 @@ import CustomQuestion from './CustomQuestion'; import QuestionInput from './QuestionInput'; interface CustomQuestionsProps { + + // Custom questions for this event. customQuestions: CustomQuestions; + + // State for if the custom question edit has completed on the backend isLoading?: boolean; + + // Callbacks for CUD operations for custom questions. onAddCustomQuestion: (type: QuestionType, ...opts: any[]) => void; onUpdateCustomQuestion: (toUpdate: Question) => void; onDeleteCustomQuestion: (type: QuestionType, toDelete: Question) => void; } +/** + * Custom Qestions editor. + */ export default class CustomQuestionsEdit extends React.Component { + + /** + * Callback for when the required state is toggled for a given question. + * + * @param {Question} question The question to be edited + */ onChangeRequired = (question: Question) => { const {onUpdateCustomQuestion} = this.props; + // Call function sent in props. onUpdateCustomQuestion({ ...question, isRequired: !question.isRequired, }); }; + /** + * Render the list of questions for a given type of question + * + * @param {QuestionType} type The type of the question to be rendered. + */ renderQuestions = (type: QuestionType) => { const {onDeleteCustomQuestion} = this.props; const {[type]: questions} = this.props.customQuestions; diff --git a/src/client/pages/EventPage/components/EventOptionsEdit.tsx b/src/client/pages/EventPage/components/EventOptionsEdit.tsx index 07be3758..982f08b1 100644 --- a/src/client/pages/EventPage/components/EventOptionsEdit.tsx +++ b/src/client/pages/EventPage/components/EventOptionsEdit.tsx @@ -5,17 +5,17 @@ import { UncontrolledTooltip } from 'reactstrap'; import ToggleSwitch from '~/components/ToggleSwitch'; interface EventOptionsProps { - // initial options of the event + // Initial options of the event options: TESCEventOptions; - // calback function for when the update button is clicked + // Callback function for when the update button is clicked onOptionsUpdate: (newOptions: TESCEventOptions) => void; - // the event these options are for + // The event these options are for event: TESCEvent; } -// the current, edited state of the options +// The current, edited state of the options interface EventOptionsState { options: TESCEventOptions; } diff --git a/src/client/pages/EventPage/components/OrganiserList.tsx b/src/client/pages/EventPage/components/OrganiserList.tsx index 79c87471..630c6073 100644 --- a/src/client/pages/EventPage/components/OrganiserList.tsx +++ b/src/client/pages/EventPage/components/OrganiserList.tsx @@ -8,22 +8,22 @@ import OrganiserSelect from '~/components/OrganiserSelect'; interface OrganiserListProps { - // list of organisers for the event + // List of organisers for the event organisers: Admin[]; - // callback function to show a modal to add a new organiser to the event + // Callback function to show a modal to add a new organiser to the event addNewOrganiser: (toAdd: AdminSelectType) => void; - // callback to add the organiser to the event + // Callback to add the organiser to the event registerNewOrganiser: (newOrganiser: NewAdminModalFormData) => void; } interface OrganiserListState { - // the new organiser to be added to the event + // The new organiser to be added to the event newOrganiser: AdminSelectType; - // boolean to track if the new organiser modal is open or not + // Boolean to track if the new organiser modal is open or not isRegisterModalOpen: boolean; } diff --git a/src/client/pages/EventPage/components/PreviewApplication.tsx b/src/client/pages/EventPage/components/PreviewApplication.tsx index 0f1013a2..33f83642 100644 --- a/src/client/pages/EventPage/components/PreviewApplication.tsx +++ b/src/client/pages/EventPage/components/PreviewApplication.tsx @@ -3,6 +3,9 @@ import { Alert } from 'reactstrap'; import ApplyPage from '../../ApplyPage'; +/** + * Renders application in preview mode. + */ class PreviewApplication extends React.Component { render() { diff --git a/src/client/pages/EventPage/components/SponsorList.tsx b/src/client/pages/EventPage/components/SponsorList.tsx index ee7a252b..66385e98 100644 --- a/src/client/pages/EventPage/components/SponsorList.tsx +++ b/src/client/pages/EventPage/components/SponsorList.tsx @@ -8,22 +8,22 @@ import SponsorSelect from '~/components/SponsorSelect'; interface SponsorListProps { - // the sponsors for this event + // The sponsors for this event sponsors: Admin[]; - // callback function to add an existing sponsor to the event + // Callback function to add an existing sponsor to the event addNewSponsor: (toAdd: AdminSelectType) => void; - // function called to create a new sponsor in the system + // Function called to create a new sponsor in the system registerNewSponsor: (newSponsor: NewAdminModalFormData) => void; } interface SponsorListState { - // the new sponsor to be added + // The new sponsor to be added newSponsor: AdminSelectType; - // boolean to track if the create sponsor modal is open or not. + // Boolean to track if the create sponsor modal is open or not. isRegisterModalOpen: boolean; } diff --git a/src/client/pages/EventPage/index.tsx b/src/client/pages/EventPage/index.tsx index 488c3846..22061d27 100644 --- a/src/client/pages/EventPage/index.tsx +++ b/src/client/pages/EventPage/index.tsx @@ -30,7 +30,7 @@ import TeamsTab from './tabs/TeamsTab'; type RouteProps = RouteComponentProps<{ - // the eventAlias for the event we want to render the dashboard for + // The eventAlias for the event we want to render the dashboard for eventAlias: string; }>; diff --git a/src/client/pages/HomePage/components/UserEvents.tsx b/src/client/pages/HomePage/components/UserEvents.tsx index c88fcc41..6e6e8a93 100644 --- a/src/client/pages/HomePage/components/UserEvents.tsx +++ b/src/client/pages/HomePage/components/UserEvents.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; interface UserEventsProps { - // the events that the user has applied to + // The events that the user has applied to events: TESCEvent[]; } diff --git a/src/client/pages/LoginPage.tsx b/src/client/pages/LoginPage.tsx index cc634dca..819e1319 100644 --- a/src/client/pages/LoginPage.tsx +++ b/src/client/pages/LoginPage.tsx @@ -28,6 +28,9 @@ interface LoginPageState extends AlertPageState { redirectToReferrer: boolean; } +/** + * Page for users to login to their accounts. + */ class LoginPage extends AlertPage { state: Readonly = { alerts: [], @@ -56,6 +59,11 @@ class LoginPage extends AlertPage { } } + /** + * Make API call for the user trying to log in. + * + * @param {LoginFormData} formProps The data in the form. + */ loginUser = (formProps: LoginFormData) => { const {loginUser, history} = this.props; return loginUser(formProps) diff --git a/src/client/pages/ResumesPage/components/ResumeList.tsx b/src/client/pages/ResumesPage/components/ResumeList.tsx index 0a45f459..1a15e0d8 100644 --- a/src/client/pages/ResumesPage/components/ResumeList.tsx +++ b/src/client/pages/ResumesPage/components/ResumeList.tsx @@ -4,13 +4,13 @@ import ToggleSwitch from '~/components/ToggleSwitch'; interface ResumeListProps { - // callback for when the compact state toggle is clicked + // Callback for when the compact state toggle is clicked onCompactChange: () => void; - // the compacted state + // The compacted state isCompacted: boolean; - // the users to render + // The users to render applicants: TESCUser[]; } @@ -20,7 +20,7 @@ interface ColumnMap { interface ResumeListState { - // columns shown on screen + // Columns shown on screen columns: ColumnMap; smallColumns: string[]; mediumColumns: string[]; diff --git a/src/client/pages/ResumesPage/index.tsx b/src/client/pages/ResumesPage/index.tsx index 9361db41..c292cb5e 100644 --- a/src/client/pages/ResumesPage/index.tsx +++ b/src/client/pages/ResumesPage/index.tsx @@ -38,7 +38,7 @@ interface ResumesPageProps { } -// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// The props of this event are the union of the react-router data, redux actions and dispatch, and the // regular props of the component type Props = RouteComponentProps<{ eventAlias: string; @@ -46,7 +46,7 @@ interface ResumesPageState { - // boolean to track compact state + // Boolean to track compact state isCompacted: boolean; } diff --git a/src/client/pages/UserPage/components/BussingModal.tsx b/src/client/pages/UserPage/components/BussingModal.tsx index 8a044d1a..fc415ea2 100644 --- a/src/client/pages/UserPage/components/BussingModal.tsx +++ b/src/client/pages/UserPage/components/BussingModal.tsx @@ -3,13 +3,13 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface BussingModalProps { - // boolean to track if the modal is open + // Boolean to track if the modal is open isOpen: boolean; - // is a bus available for this school? + // Is a bus available for this school? availableBus?: string; - // callback for when the bus is clicked + // Callback for when the bus is clicked onChooseBus: (choice: boolean) => void; } diff --git a/src/client/pages/UserPage/components/RSVPModal.tsx b/src/client/pages/UserPage/components/RSVPModal.tsx index e213845e..dc2655c2 100644 --- a/src/client/pages/UserPage/components/RSVPModal.tsx +++ b/src/client/pages/UserPage/components/RSVPModal.tsx @@ -4,16 +4,16 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; interface RSVPModalProps { - // toggle function for the model + // Toggle function for the model toggle: () => void; - // variable to track open or close state of the modal + // Variable to track open or close state of the modal isOpen: boolean; - // callback function for when the status is chosen + // Callback function for when the status is chosen onChooseStatus: (statusChoice: boolean) => void; - // the event for which the current application is on + // The event for which the current application is on event: TESCEvent; } diff --git a/src/client/pages/UserPage/components/UserProfile.tsx b/src/client/pages/UserPage/components/UserProfile.tsx index b19193b4..5ff720c1 100644 --- a/src/client/pages/UserPage/components/UserProfile.tsx +++ b/src/client/pages/UserPage/components/UserProfile.tsx @@ -25,13 +25,13 @@ export interface UserProfileFormData { interface UserProfileProps { - // the user for which the profile is rendered + // The user for which the profile is rendered user: TESCUser; - // the event for which the application is on + // The event for which the application is on event: TESCEvent; - // callback function to toggle RSVP status + // Callback function to toggle RSVP status toggleRSVP: () => void; } diff --git a/src/client/pages/UserPage/index.tsx b/src/client/pages/UserPage/index.tsx index c215f767..05b9e77e 100644 --- a/src/client/pages/UserPage/index.tsx +++ b/src/client/pages/UserPage/index.tsx @@ -33,7 +33,7 @@ const mapDispatchToProps = (dispatch: ApplicationDispatch) => bindActionCreators interface UserPageProps { } -// the props of this event are the union of the react-router data, redux actions and dispatch, and the +// The props of this event are the union of the react-router data, redux actions and dispatch, and the // regular props of the component type Props = RouteComponentProps<{ eventAlias: string; From fd060ef5e9fc5846347cfb215a788f0eb628cd08 Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 29 Aug 2020 20:12:18 -0400 Subject: [PATCH 30/31] Added exportEmails Fn --- .../pages/EventPage/tabs/ActionsTab.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/client/pages/EventPage/tabs/ActionsTab.tsx b/src/client/pages/EventPage/tabs/ActionsTab.tsx index c21094e3..ae7a58b0 100644 --- a/src/client/pages/EventPage/tabs/ActionsTab.tsx +++ b/src/client/pages/EventPage/tabs/ActionsTab.tsx @@ -39,6 +39,25 @@ export default class ActionsTab extends EventPageTab { }); } + /** + * This function is called when the user clicks the 'Export All Emails' button. + */ + exportEmails = () => { + const eventAlias = this.props.event.alias; + exportUsers(eventAlias, true) + .end((err, res) => { + // Download as file + const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', `${eventAlias}-${Date.now()}.csv`); + document.body.appendChild(link); + + link.click(); + }); + } + /** * This function is called when the Bulk Change button is clicked on the form. * @param {BulkChangeFormData} values the values of the Bulk Change Form From 039fb658eda24028c6eaaf16c66fde07331fa7bf Mon Sep 17 00:00:00 2001 From: Subhankar Panda Date: Sat, 29 Aug 2020 20:26:57 -0400 Subject: [PATCH 31/31] Fixed exportEmails --- src/client/pages/EventPage/tabs/ActionsTab.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/client/pages/EventPage/tabs/ActionsTab.tsx b/src/client/pages/EventPage/tabs/ActionsTab.tsx index 8ce088be..ca2d3401 100644 --- a/src/client/pages/EventPage/tabs/ActionsTab.tsx +++ b/src/client/pages/EventPage/tabs/ActionsTab.tsx @@ -46,10 +46,7 @@ export default class ActionsTab extends EventPageTab { const eventAlias = this.props.event.alias; exportUsers(eventAlias, true) - // call API's exportUsers. - // surprisingly, this is okay syntax because exportUsers and this.exportUsers are different - // TODO: name API call better - exportUsers(eventAlias, false) + exportUsers(eventAlias, true) .end((err, res) => { // Download as file const blob = new Blob([res.text], { type: 'text/csv;charset=utf-8;' });