Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
eae331b
Added root-level frontend README
subhankar-panda May 18, 2019
593ee9d
Tree
subhankar-panda May 18, 2019
5b9d6f1
Removed links
subhankar-panda May 18, 2019
f63cf7e
Added pages documentation
subhankar-panda May 19, 2019
af31bfe
Documentation for AdminsPage and ApplyPage done
subhankar-panda May 19, 2019
aa50934
DashboardPage finished, TODO SettingsTab + StatisticsTab
subhankar-panda May 19, 2019
7084fa3
Working through DashboardPage/components
subhankar-panda May 25, 2019
21232d2
Event, HomePage Documentation complete
subhankar-panda May 25, 2019
4e151e4
NewEventPage Documentation Complete
subhankar-panda May 25, 2019
adc8bb3
TODO RSVPConfirm onwards and UsersPage
subhankar-panda May 26, 2019
8bc1755
User and UsersPage done
subhankar-panda May 26, 2019
4314fef
Almost done with pages application
subhankar-panda May 26, 2019
e253abe
Ran tslint --fix
subhankar-panda Aug 8, 2020
9af8651
Ran tslint --fix
subhankar-panda Aug 8, 2020
ea58fb4
Fixed merge conflict:
subhankar-panda Aug 29, 2020
8437aea
Added root-level frontend README
subhankar-panda May 18, 2019
93e6d47
Tree
subhankar-panda May 18, 2019
8f5f642
Removed links
subhankar-panda May 18, 2019
e35f0c2
Added pages documentation
subhankar-panda May 19, 2019
f25ae32
Documentation for AdminsPage and ApplyPage done
subhankar-panda May 19, 2019
2fc4eb2
DashboardPage finished, TODO SettingsTab + StatisticsTab
subhankar-panda May 19, 2019
4da75a5
Working through DashboardPage/components
subhankar-panda May 25, 2019
5c157b2
Event, HomePage Documentation complete
subhankar-panda May 25, 2019
be514e0
NewEventPage Documentation Complete
subhankar-panda May 25, 2019
c18ed86
TODO RSVPConfirm onwards and UsersPage
subhankar-panda May 26, 2019
c7b4585
User and UsersPage done
subhankar-panda May 26, 2019
55ed7de
Almost done with pages application
subhankar-panda May 26, 2019
307d9ad
Ran tslint --fix
subhankar-panda Aug 8, 2020
8337ffc
Ran tslint --fix
subhankar-panda Aug 8, 2020
b4ca736
Last pass for frontend docs
subhankar-panda Aug 30, 2020
fd060ef
Added exportEmails Fn
subhankar-panda Aug 30, 2020
5fd58f4
Fixed Merge
subhankar-panda Aug 30, 2020
039fb65
Fixed exportEmails
subhankar-panda Aug 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/client/PrivateUserRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -16,6 +21,9 @@ interface PrivateUserRouteProps {

type Props = RouteProps & StateProps & PrivateUserRouteProps;

/**
* Lock down a view by authentication state.
*/
class PrivateUserRoute extends React.Component<Props> {
render() {
return (
Expand Down
40 changes: 40 additions & 0 deletions src/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## 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)


### 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.
├── auth
* 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
* 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.
├── 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
```
14 changes: 14 additions & 0 deletions src/client/components/EventForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<EventFormData, EventFormProps> & EventFormProps;

/**
* This is the redux-form to create a new event
*/
class EventForm extends React.Component<Props> {

/**
* Create a file droppable field for the event logo
*
* @returns {Component}
*/
createLogoUpload() {
return (
<Field
Expand All @@ -36,6 +45,11 @@ class EventForm extends React.Component<Props> {
);
}

/**
* Show an alert that flags the event as a non-TESC hosted event.
*
* @returns {React.StatelessComponent}
*/
showThirdPartyText: React.StatelessComponent<WrappedFieldsProps> = ({values}) => {
if (values && values.organisedBy && values.organisedBy.input.value !== 'TESC') {
return (
Expand Down
22 changes: 13 additions & 9 deletions src/client/components/Fields.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { UserDiversityOptions, UserYearOptions, UserGenderOptions, UserPronounOptions, UserShirtSizeOptions } from '@Shared/UserEnums';
import { UserDiversityOptions,
UserYearOptions,
UserGenderOptions,
UserPronounOptions,
UserShirtSizeOptions } from '@Shared/UserEnums';
Comment on lines +2 to +5
Copy link
Contributor

Choose a reason for hiding this comment

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

Can these be aligned with line 1? Does ESLint fix these kind of formatting issues?

Since this is a refactoring PR I'm gonna be ocd about these kind of stuff 😛

import React from 'react';
import { Field, WrappedFieldProps } from 'redux-form';
import majors from '~/static/Majors.json';
Expand Down Expand Up @@ -48,7 +52,7 @@ export function errorClass(className: string, touched: boolean, error: boolean)
}

export const errorTextInput: React.StatelessComponent<CustomFieldProps> = ({ input, className, placeholder, type,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);
return (
<div>
Expand Down Expand Up @@ -81,7 +85,7 @@ export const errorRadio: React.StatelessComponent<CustomFieldProps> = ({ input,
};

export const errorTextArea: React.StatelessComponent<CustomFieldProps> = ({ input, className, placeholder, maxLength,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);
return (
<div>
Expand Down Expand Up @@ -122,7 +126,7 @@ export const errorMonthPicker: React.StatelessComponent<CustomFieldProps> = ({ i
};

export const errorTShirtSizePicker: React.StatelessComponent<CustomFieldProps> = ({ 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);
Expand All @@ -143,7 +147,7 @@ export const errorTShirtSizePicker: React.StatelessComponent<CustomFieldProps> =
};

export const errorGenderPicker: React.StatelessComponent<CustomFieldProps> = ({ input, className, type,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);

return (
Expand All @@ -162,7 +166,7 @@ export const errorGenderPicker: React.StatelessComponent<CustomFieldProps> = ({
};

export const errorPronounPicker: React.StatelessComponent<CustomFieldProps> = ({ input, className, type,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);

return (
Expand All @@ -181,7 +185,7 @@ export const errorPronounPicker: React.StatelessComponent<CustomFieldProps> = ({
};

export const errorDiversityOptions: React.StatelessComponent<CustomFieldProps> = ({ input, className, type,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);

return (
Expand All @@ -200,7 +204,7 @@ export const errorDiversityOptions: React.StatelessComponent<CustomFieldProps> =
};

export const errorYearPicker: React.StatelessComponent<CustomFieldProps> = ({ input, className, type,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);

return (
Expand All @@ -219,7 +223,7 @@ export const errorYearPicker: React.StatelessComponent<CustomFieldProps> = ({ in
};

export const errorMajorPicker: React.StatelessComponent<CustomFieldProps> = ({ input, className, type,
meta: { touched, error } }) => {
meta: { touched, error } }) => {
const errorClassName = errorClass(className, touched, error);

return (
Expand Down
3 changes: 1 addition & 2 deletions src/client/components/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ export default class Hero extends React.Component<HeroProps> {
}

return (
<div className={heroClass} style={heroStyle}>
</div>
<div className={heroClass} style={heroStyle}/>
);
}
}
6 changes: 3 additions & 3 deletions src/client/components/User.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -114,7 +114,7 @@ class User extends React.Component<Props> {
className="form-control"
component="select"
type={fieldType}>
<option></option>
<option/>
{options.map((o, i) => <option key={`opt-${i}`}>{o}</option>)}
</Field>
</div>
Expand Down Expand Up @@ -343,7 +343,7 @@ class User extends React.Component<Props> {
{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')}
</div>
</span>
Expand Down
17 changes: 8 additions & 9 deletions src/client/data/AdminApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -195,7 +195,6 @@ export const editExistingEvent = (id: string, event: Partial<EventFormData>) =>
closeTimeDay
)).toISOString(true);


const promiseReq = request
.post(`/events/edit/${id}`)
.set('Authorization', cookies.get(CookieTypes.admin.token))
Expand All @@ -204,14 +203,14 @@ export const editExistingEvent = (id: string, event: Partial<EventFormData>) =>
closeTime,
} as RegisterEventRequest))
.use(adminApiPrefix)
.use(nocache)
.use(nocache);

if (logo) {
promiseReq.attach('logo', logo[0])
if (logo) {
promiseReq.attach('logo', logo[0]);
Copy link
Contributor

Choose a reason for hiding this comment

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

misaligned

}

return promisify<void>(promiseReq);
}
return promisify<void>(promiseReq);
};

/**
* Request to register a new admin.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -450,4 +449,4 @@ export const sendWaitlistEmail = (user: TESCUser) =>
.send({ user } as StatusEmailRequest)
.use(adminApiPrefix)
.use(nocache)
);
);
2 changes: 1 addition & 1 deletion src/client/layouts/public.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ class PublicLayout extends React.Component {
}
}

export default PublicLayout;
export default PublicLayout;
4 changes: 2 additions & 2 deletions src/client/layouts/sponsor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ class SponsorLayout extends React.Component<Props, SponsorLayoutState> {
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);
link.click();

hideLoading();
this.setState({
isDownloading: false
isDownloading: false,
});
})
.catch(console.error);
Expand Down
1 change: 1 addition & 0 deletions src/client/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const store = createStore(reducer,
applyMiddleware(reduxThunk as ThunkMiddleware<ApplicationState, AnyAction>)
));

// Configuring the application for routing, cookies, and redux.
render(
(
<CookiesProvider>
Expand Down
4 changes: 3 additions & 1 deletion src/client/pages/AdminsPage/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)<Admin[]>();

// Replace the `admins` state with the payload
export const replaceAdmins = createStandardAction(Types.REPLACE_ADMINS)<Admin[]>();
24 changes: 17 additions & 7 deletions src/client/pages/AdminsPage/components/AdminList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -19,12 +26,15 @@ interface AdminListState {
columns: DisplayColumnMap;
}

/**
* This component renders a list of admins on the admins page
*/
export default class AdminList extends React.Component<AdminListProps, AdminListState> {
state: Readonly<AdminListState> = {
columns: {
username: 'Username',
role: 'Role',
lastAccessed: 'Last Accessed'
lastAccessed: 'Last Accessed',
},
};

Expand All @@ -46,19 +56,19 @@ export default class AdminList extends React.Component<AdminListProps, AdminList
renderAdmin = (admin: Admin) => {
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()
Copy link
Contributor

Choose a reason for hiding this comment

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

can you align these lines somehow

: 'Never Logged In',
};
return Object.keys(columns).map(column => (
<td key={column} className="admin-list__value">
{/*
// TODO: Fix accessing dynamic properties
// @ts-ignore */}
{adminToBeRendered[column]}
</td>)
)
);
}

render() {
Expand Down
Loading