├── .nvmrc ├── .prettierignore ├── public ├── _redirects ├── favicon │ ├── favicon.ico │ ├── favicon.png │ ├── favicon-128.png │ ├── mstile-70x70.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── apple-touch-icon.png │ ├── favicon-196x196.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ └── apple-touch-icon-152x152.png └── fonts │ ├── lineto-brown-bold.ttf │ └── lineto-brown-regular.ttf ├── .yarnrc.yml ├── src ├── react-app-env.d.ts ├── config │ ├── pageType.ts │ ├── inviteInfo.ts │ ├── settings.ts │ ├── attendanceOptions.ts │ ├── resumeResponse.ts │ ├── pronouns.ts │ ├── teamGETResponse.ts │ ├── jobInterests.ts │ ├── searchOptions.ts │ ├── shirtSizes.ts │ ├── genders.ts │ ├── previousHackathons.ts │ ├── dietaryRestrictions.ts │ ├── team.ts │ ├── travelStatus.ts │ ├── authToken.ts │ ├── ethnicity.ts │ ├── hackerStatus.ts │ ├── hackerReviewerStatus.ts │ ├── travel.ts │ ├── gradYears.ts │ ├── validationError.ts │ ├── degrees.ts │ ├── reviewers.ts │ ├── searchParameter.ts │ ├── frontendRoutes.ts │ ├── index.ts │ ├── statsResponse.ts │ ├── skills.ts │ ├── APIRoute.ts │ └── majors.ts ├── api │ ├── README.md │ ├── APIResponse.ts │ ├── index.ts │ ├── search.ts │ ├── emails.ts │ ├── settings.ts │ ├── team.ts │ ├── api.ts │ ├── sponsor.ts │ └── travel.ts ├── assets │ ├── gifs │ │ ├── markasread.gif │ │ ├── screenshare.gif │ │ ├── setnickname.gif │ │ └── notifstomention.gif │ └── images │ │ ├── logo_text.png │ │ ├── martlet-text-13.png │ │ ├── martlet-text-13-light.png │ │ ├── copy-icon.svg │ │ ├── done.svg │ │ ├── sidebar-app.svg │ │ ├── backarrow.svg │ │ ├── fb-logo.svg │ │ ├── passwordReset.svg │ │ ├── upload.svg │ │ ├── sidebar-profile.svg │ │ ├── sidebar-bus.svg │ │ ├── sidebar-team.svg │ │ ├── twitter-logo.svg │ │ ├── github-logo.svg │ │ ├── share-icon.svg │ │ ├── dashboard-confirm.svg │ │ ├── sidebar-home.svg │ │ ├── key.svg │ │ ├── dashboard-hackpass.svg │ │ ├── ig-logo.svg │ │ ├── logo.svg │ │ ├── dashboard-application.svg │ │ └── dashboard-account.svg ├── util │ ├── index.ts │ └── LocalCache.ts ├── features │ ├── Nav │ │ ├── Icon.tsx │ │ ├── IconContainer.tsx │ │ ├── Links.tsx │ │ ├── LoginButton.tsx │ │ ├── NavLink.tsx │ │ ├── Nav.tsx │ │ ├── LogoutButton.tsx │ │ └── Burger.tsx │ ├── Onboarding │ │ ├── Container.tsx │ │ ├── StyledGIF.tsx │ │ ├── NavContainer.tsx │ │ ├── OnboardingNav.tsx │ │ ├── Content.tsx │ │ ├── Text.tsx │ │ ├── NavLink.tsx │ │ └── Onboarding.tsx │ ├── Search │ │ ├── Context.tsx │ │ └── HackerSelect.tsx │ ├── Team │ │ ├── validationSchema.ts │ │ ├── MemberList │ │ │ ├── MemberList.tsx │ │ │ └── MemberItem.tsx │ │ └── TeamDescription.tsx │ ├── Sponsor │ │ ├── validationSchema.ts │ │ └── SocialMediaBar.tsx │ ├── Invite │ │ ├── validationSchema.ts │ │ ├── ExistingInvitesTable.tsx │ │ └── ExistingInvites.tsx │ ├── Login │ │ ├── ForgotPasswordLink.tsx │ │ └── SignUpLink.tsx │ ├── SingleHacker │ │ ├── SingleHackerField.tsx │ │ ├── SingleHackerParagraph.tsx │ │ ├── SingleHackerSection.tsx │ │ └── SingleHackerLink.tsx │ ├── Account │ │ ├── AlreadyHaveAccount.tsx │ │ └── validationSchema.ts │ ├── HackPass │ │ └── Pass.tsx │ ├── Application │ │ ├── PaginationHeader │ │ │ ├── SeperatingBar.tsx │ │ │ ├── NumberPageText.tsx │ │ │ ├── NumberBubble.tsx │ │ │ └── PaginationHeader.tsx │ │ └── ResumeComponent.tsx │ ├── Checkin │ │ ├── Reader.tsx │ │ └── Email.tsx │ └── Dashboard │ │ ├── DashboardText.tsx │ │ ├── SponsorDashboard.tsx │ │ └── View.tsx ├── shared │ ├── Elements │ │ ├── Bold.tsx │ │ ├── LeftContainer.tsx │ │ ├── Textarea.tsx │ │ ├── LinkDuo.tsx │ │ ├── Image.tsx │ │ ├── index.ts │ │ ├── HorizontalSpacer.tsx │ │ ├── GridTwoColumn.tsx │ │ ├── H2.tsx │ │ ├── MaxWidthBox.tsx │ │ ├── Card.tsx │ │ ├── H1.tsx │ │ ├── StyledModal.tsx │ │ ├── StyledModalSmall.tsx │ │ ├── TextButton.tsx │ │ ├── Paragraph.tsx │ │ ├── BackgroundImage.tsx │ │ ├── ConfirmModal.tsx │ │ ├── ViewPDF.tsx │ │ └── Clipboard.tsx │ ├── Form │ │ ├── FileInput.tsx │ │ ├── SecondaryInfoText.tsx │ │ ├── FormikElements │ │ │ ├── index.ts │ │ │ ├── Error.tsx │ │ │ ├── Input.tsx │ │ │ ├── FormattedNumber.tsx │ │ │ ├── LongTextInput.tsx │ │ │ ├── PhoneNumberInput.tsx │ │ │ └── PhoneNumberContainer.tsx │ │ ├── Form.tsx │ │ ├── RequiredInputLabel.tsx │ │ ├── Input.tsx │ │ ├── Checkbox.tsx │ │ ├── StyledNumberFormat.tsx │ │ ├── SubmitBtn.tsx │ │ ├── Label.tsx │ │ ├── index.ts │ │ ├── NumberFormatInput.tsx │ │ ├── StyledSelect.tsx │ │ ├── validationErrorGenerator.tsx │ │ ├── EmailInput.tsx │ │ ├── StyledCreatableSelect.tsx │ │ ├── LabelText.tsx │ │ ├── PasswordInput.tsx │ │ └── Autosuggest.tsx │ ├── HOC │ │ ├── index.ts │ │ ├── withToaster.tsx │ │ ├── withThemeProvider.tsx │ │ ├── withBackground.tsx │ │ ├── withNavbar.tsx │ │ ├── withContext.tsx │ │ ├── withTokenRedirect.tsx │ │ ├── withSponsorRedirect.tsx │ │ └── withHackerRedirect.tsx │ └── Styles │ │ ├── style.d.ts │ │ ├── theme.ts │ │ ├── inputStyles.ts │ │ └── GlobalStyles.tsx ├── pages │ ├── Admin │ │ ├── Search.tsx │ │ └── Invite.tsx │ ├── Sponsor │ │ ├── Search.tsx │ │ ├── Onboarding.tsx │ │ ├── Edit.tsx │ │ └── Create.tsx │ ├── Travel │ │ ├── DollarAmount.tsx │ │ ├── TravelStatusNone.tsx │ │ ├── TravelStatusClaimed.tsx │ │ ├── TravelStatusPolicy.tsx │ │ ├── TravelStatusBus.tsx │ │ └── TravelStatusOffered.tsx │ ├── Application │ │ ├── Edit.tsx │ │ └── Create.tsx │ ├── _error.tsx │ ├── Account │ │ ├── Edit.tsx │ │ └── Create.tsx │ └── index.tsx ├── App.test.tsx └── index.tsx ├── .yarn └── install-state.gz ├── .idea ├── .gitignore ├── vcs.xml ├── modules.xml ├── dashboard.iml └── mongoSettings.xml ├── .vscode ├── extensions.json ├── settings.json └── launch.json ├── config-overrides.js ├── .prettierrc ├── .gitignore ├── tslint.json ├── tsconfig.json ├── docs ├── pull_request_template.md └── CONTRIBUTING.md ├── LICENSE └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.10.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.yarn/install-state.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/.yarn/install-state.gz -------------------------------------------------------------------------------- /public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon.ico -------------------------------------------------------------------------------- /public/favicon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon.png -------------------------------------------------------------------------------- /src/config/pageType.ts: -------------------------------------------------------------------------------- 1 | export type PageType = 'Home' | 'Application' | 'Profile' | 'Team' | 'Travel'; 2 | -------------------------------------------------------------------------------- /src/api/README.md: -------------------------------------------------------------------------------- 1 | # Folder Structure 2 | 3 | This folder is for all classes that interact with the hackerAPI. -------------------------------------------------------------------------------- /src/config/inviteInfo.ts: -------------------------------------------------------------------------------- 1 | export interface IInviteInfo { 2 | email: string; 3 | accountType: string; 4 | } 5 | -------------------------------------------------------------------------------- /public/favicon/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon-128.png -------------------------------------------------------------------------------- /public/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /src/assets/gifs/markasread.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/gifs/markasread.gif -------------------------------------------------------------------------------- /src/assets/gifs/screenshare.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/gifs/screenshare.gif -------------------------------------------------------------------------------- /src/assets/gifs/setnickname.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/gifs/setnickname.gif -------------------------------------------------------------------------------- /src/assets/images/logo_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/images/logo_text.png -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/favicon-196x196.png -------------------------------------------------------------------------------- /public/fonts/lineto-brown-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/fonts/lineto-brown-bold.ttf -------------------------------------------------------------------------------- /src/assets/gifs/notifstomention.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/gifs/notifstomention.gif -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LocalCache'; 2 | export * from './UserInfoHelperFunctions'; 3 | export * from './util'; 4 | -------------------------------------------------------------------------------- /public/fonts/lineto-brown-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/fonts/lineto-brown-regular.ttf -------------------------------------------------------------------------------- /src/assets/images/martlet-text-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/images/martlet-text-13.png -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/public/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /src/assets/images/martlet-text-13-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackmcgill/dashboard/HEAD/src/assets/images/martlet-text-13-light.png -------------------------------------------------------------------------------- /src/api/APIResponse.ts: -------------------------------------------------------------------------------- 1 | export class APIResponse { 2 | public data: T; 3 | public message: string; 4 | } 5 | export default APIResponse; 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "ms-vscode.vscode-typescript-tslint-plugin" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/config/settings.ts: -------------------------------------------------------------------------------- 1 | export interface ISetting { 2 | openTime: string; 3 | closeTime: string; 4 | confirmTime: string; 5 | isRemote: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /src/config/attendanceOptions.ts: -------------------------------------------------------------------------------- 1 | export enum AttendenceOptions { 2 | REMOTE = 'Remote', 3 | INPERSON = 'In Person', 4 | } 5 | export default AttendenceOptions; 6 | -------------------------------------------------------------------------------- /src/config/resumeResponse.ts: -------------------------------------------------------------------------------- 1 | export interface IResumeResponse { 2 | id: string; 3 | resume: [ 4 | { 5 | data: Uint8Array; 6 | } 7 | ]; 8 | } 9 | -------------------------------------------------------------------------------- /src/features/Nav/Icon.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Icon = styled.img` 4 | height: 72px; 5 | `; 6 | 7 | export default Icon; 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "files.insertFinalNewline": true, 4 | "files.trimFinalNewlines": true, 5 | "editor.tabSize": 4 6 | } 7 | -------------------------------------------------------------------------------- /src/config/pronouns.ts: -------------------------------------------------------------------------------- 1 | export enum Pronouns { 2 | SHE_HER = 'She/Her', 3 | HE_HIM = 'He/Him', 4 | THEY_THEM = 'They/Them', 5 | } 6 | 7 | export default Pronouns; 8 | -------------------------------------------------------------------------------- /src/config/teamGETResponse.ts: -------------------------------------------------------------------------------- 1 | import { IMemberName, ITeam } from './team'; 2 | 3 | export interface ITeamResponse { 4 | team: ITeam; 5 | members: IMemberName[]; 6 | } 7 | -------------------------------------------------------------------------------- /src/shared/Elements/Bold.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Bold = styled.span` 4 | font-weight: bolder; 5 | `; 6 | 7 | export default Bold; 8 | -------------------------------------------------------------------------------- /src/config/jobInterests.ts: -------------------------------------------------------------------------------- 1 | export enum JobInterest { 2 | FULL_TIME = 'Full Time', 3 | INTERNSHIP = 'Internship', 4 | NONE = 'None', 5 | } 6 | export default JobInterest; 7 | -------------------------------------------------------------------------------- /src/shared/Form/FileInput.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const FileInput = styled.input` 4 | margin-left: 10px; 5 | `; 6 | 7 | export default FileInput; 8 | -------------------------------------------------------------------------------- /src/config/searchOptions.ts: -------------------------------------------------------------------------------- 1 | export interface ISearchOptions { 2 | sort?: 'asc' | 'desc'; 3 | page?: number; 4 | limit?: number; 5 | sort_by?: string; 6 | expand?: boolean; 7 | } 8 | -------------------------------------------------------------------------------- /src/config/shirtSizes.ts: -------------------------------------------------------------------------------- 1 | export enum ShirtSize { 2 | XS = 'XS', 3 | S = 'S', 4 | M = 'M', 5 | L = 'L', 6 | XL = 'XL', 7 | XXL = 'XXL', 8 | } 9 | export default ShirtSize; 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const { 2 | override, 3 | addBabelPlugin 4 | } = require('customize-cra') 5 | 6 | module.exports = override( 7 | addBabelPlugin('styled-jsx/babel') 8 | ); 9 | -------------------------------------------------------------------------------- /src/config/genders.ts: -------------------------------------------------------------------------------- 1 | export enum Genders { 2 | FEMALE = 'Female', 3 | MALE = 'Male', 4 | NON_BINARY = 'Non-binary', 5 | NO_ANSWER = 'Prefer not to answer', 6 | } 7 | export default Genders; 8 | -------------------------------------------------------------------------------- /src/features/Onboarding/Container.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.section` 4 | margin-bottom: 12rem; 5 | `; 6 | 7 | export default Container; 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "semi": true, 6 | "trailingComma": "es5", 7 | "arrowParens": "always", 8 | "jsxBracketSameLine": false 9 | } 10 | -------------------------------------------------------------------------------- /src/features/Search/Context.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ISponsor } from '../../config'; 3 | 4 | const NomineeContext = React.createContext({} as ISponsor | undefined); 5 | 6 | export default NomineeContext; 7 | -------------------------------------------------------------------------------- /src/pages/Admin/Search.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SearchContainer from '../../features/Search/Search'; 4 | 5 | const AdminSearchPage: React.FC = () => ; 6 | 7 | export default AdminSearchPage; 8 | -------------------------------------------------------------------------------- /src/pages/Sponsor/Search.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SearchContainer from '../../features/Search/Search'; 4 | 5 | const SponsorSearchPage: React.FC = () => ; 6 | 7 | export default SponsorSearchPage; 8 | -------------------------------------------------------------------------------- /src/config/previousHackathons.ts: -------------------------------------------------------------------------------- 1 | export const PreviousHackathons: { [key: string]: string } = { 2 | '0': '0', 3 | '1': '1', 4 | '2': '2', 5 | '3': '3', 6 | '4': '4', 7 | '5+': '5', 8 | }; 9 | export default PreviousHackathons; 10 | -------------------------------------------------------------------------------- /src/shared/HOC/index.ts: -------------------------------------------------------------------------------- 1 | export * from './withAuthRedirect'; 2 | export * from './withHackerRedirect'; 3 | export * from './withNavbar'; 4 | export * from './withThemeProvider'; 5 | export * from './withToaster'; 6 | export * from './withTokenRedirect'; 7 | -------------------------------------------------------------------------------- /src/shared/Elements/LeftContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@rebass/grid'; 2 | import styled from 'styled-components'; 3 | 4 | export const LeftContainer = styled(Box)` 5 | float: left; 6 | width: 50%; 7 | `; 8 | 9 | export default LeftContainer; 10 | -------------------------------------------------------------------------------- /src/features/Team/validationSchema.ts: -------------------------------------------------------------------------------- 1 | import { object, string } from 'yup'; 2 | 3 | const getValidationSchema = () => { 4 | return object().shape({ 5 | name: string().required('Required'), 6 | }); 7 | }; 8 | 9 | export default getValidationSchema; 10 | -------------------------------------------------------------------------------- /src/pages/Sponsor/Onboarding.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import OnboardingContainer from '../../features/Onboarding/Onboarding'; 4 | 5 | const SponsorOnboardingPage: React.FC = () => ; 6 | 7 | export default SponsorOnboardingPage; 8 | -------------------------------------------------------------------------------- /src/shared/Form/SecondaryInfoText.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const SecondaryInfoText = styled.span` 4 | color: ${(props) => props.theme.colors.purple}; 5 | display: inline-block; 6 | margin-left: 5px; 7 | font-size: 12px; 8 | `; 9 | -------------------------------------------------------------------------------- /src/features/Sponsor/validationSchema.ts: -------------------------------------------------------------------------------- 1 | import { object, string } from 'yup'; 2 | 3 | const getValidationSchema = () => { 4 | return object().shape({ 5 | company: string().required('Required'), 6 | }); 7 | }; 8 | 9 | export default getValidationSchema; 10 | -------------------------------------------------------------------------------- /src/shared/Form/FormikElements/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Checkbox'; 2 | export * from './Error'; 3 | export * from './Input'; 4 | export * from './FormattedNumber'; 5 | export * from './LongTextInput'; 6 | export * from './Select'; 7 | export * from './PhoneNumberInput'; 8 | -------------------------------------------------------------------------------- /src/features/Nav/IconContainer.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const IconContainer = styled.div` 4 | cursor: pointer; 5 | margin-left: 40px; 6 | padding-top: 8px; 7 | align-self: center; 8 | `; 9 | 10 | export default IconContainer; 11 | -------------------------------------------------------------------------------- /src/shared/Form/Form.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface IFormProps { 4 | width?: string | number; 5 | } 6 | 7 | export const Form = styled.form` 8 | width: ${(props) => props.width || '100%'}; 9 | `; 10 | 11 | export default Form; 12 | -------------------------------------------------------------------------------- /src/assets/images/copy-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = ReactDOM.createRoot(document.createElement('div')); 7 | div.render(); 8 | div.unmount(); 9 | }); 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/features/Onboarding/StyledGIF.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const StyledGIF = styled.img` 4 | width: 100%; 5 | 6 | @media only screen and (min-width: ${(props) => props.theme.screens.smUp}) { 7 | width: 80%; 8 | } 9 | `; 10 | 11 | export default StyledGIF; 12 | -------------------------------------------------------------------------------- /src/shared/Elements/Textarea.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import inputStyles from '../Styles/inputStyles'; 3 | 4 | export const Textarea = styled.textarea` 5 | ${inputStyles} 6 | padding: 10px 18px; 7 | min-height: 200px; 8 | resize: none; 9 | `; 10 | export default Textarea; 11 | -------------------------------------------------------------------------------- /src/config/dietaryRestrictions.ts: -------------------------------------------------------------------------------- 1 | export enum DietaryRestriction { 2 | NONE = 'None', 3 | DAIRY_FREE = 'Dairy-Free', 4 | GLUTEN_FREE = 'Gluten-Free', 5 | HALAL = 'Halal', 6 | KOSHER = 'Kosher', 7 | VEGAN = 'Vegan', 8 | VEGETARIAN = 'Vegetarian', 9 | } 10 | export default DietaryRestriction; 11 | -------------------------------------------------------------------------------- /src/config/team.ts: -------------------------------------------------------------------------------- 1 | export interface ITeam { 2 | name: string; 3 | members: string[]; 4 | devpostURL?: string; 5 | projectName?: string; 6 | } 7 | 8 | export interface IMemberName { 9 | id: string; 10 | firstName: string; 11 | lastName: string; 12 | school?: string; 13 | status: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/shared/Form/RequiredInputLabel.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const RequiredInputLabel = styled.span` 4 | color: ${(props) => props.theme.colors.red}; 5 | display: inline-block; 6 | font-weight: lighter; 7 | margin-left: 5px; 8 | `; 9 | export default RequiredInputLabel; 10 | -------------------------------------------------------------------------------- /src/config/travelStatus.ts: -------------------------------------------------------------------------------- 1 | export enum TravelStatus { 2 | TRAVEL_STATUS_NONE = 'None', 3 | TRAVEL_STATUS_BUS = 'Bus', 4 | TRAVEL_STATUS_OFFERED = 'Offered', 5 | TRAVEL_STATUS_VALID = 'Valid', 6 | TRAVEL_STATUS_INVALID = 'Invalid', 7 | TRAVEL_STATUS_CLAIMED = 'Claimed', 8 | } 9 | export default TravelStatus; 10 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import { unregister } from './registerServiceWorker'; 5 | 6 | const root = ReactDOM.createRoot( 7 | document.getElementById('root') as HTMLElement 8 | ); 9 | root.render(); 10 | unregister(); 11 | -------------------------------------------------------------------------------- /src/assets/images/done.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/shared/Form/Input.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { inputStyles } from '../Styles/inputStyles'; 3 | import { IInputProps } from '../Styles/inputStyles'; 4 | 5 | const inputBase = styled.input``; 6 | 7 | export const Input = styled(inputBase)` 8 | ${inputStyles} 9 | `; 10 | 11 | export default Input; 12 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './APIResponse'; 2 | export * from './account'; 3 | export * from './api'; 4 | export * from './auth'; 5 | export * from './endpoint'; 6 | export * from './hacker'; 7 | export * from './travel'; 8 | export * from './search'; 9 | export * from './settings'; 10 | export * from './sponsor'; 11 | export * from './emails'; 12 | -------------------------------------------------------------------------------- /src/features/Invite/validationSchema.ts: -------------------------------------------------------------------------------- 1 | import { object, string } from 'yup'; 2 | 3 | export const getValidationSchema = () => { 4 | return object().shape({ 5 | email: string().email('Must be a valid email.').required('Required'), 6 | accountType: string().required('Required'), 7 | }); 8 | }; 9 | 10 | export default getValidationSchema; 11 | -------------------------------------------------------------------------------- /src/shared/Form/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Checkbox = styled.input.attrs({ type: 'checkbox' })` 4 | border-radius: 4px; 5 | width: 22px; 6 | height: 22px; 7 | background: ${(props) => props.theme.colors.white}; 8 | position: relative; 9 | vertical-align: middle; 10 | bottom: 1px; 11 | `; 12 | -------------------------------------------------------------------------------- /src/shared/Form/FormikElements/Error.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import theme from '../../Styles/theme'; 3 | 4 | const FormikError = styled.div` 5 | color: ${theme.colors.red}; 6 | font-size: 16px; 7 | margin-top: -26px; 8 | margin-bottom: 24px; 9 | text-align: left; 10 | `; 11 | 12 | export { FormikError as Error }; 13 | -------------------------------------------------------------------------------- /src/config/authToken.ts: -------------------------------------------------------------------------------- 1 | import QueryString from 'query-string'; 2 | 3 | export function getTokenFromQuery(): string { 4 | const queries = QueryString.parse(window.location.search); 5 | if (!queries.token) { 6 | throw new Error('Token not present in the query body'); 7 | } 8 | return queries.token as string; 9 | } 10 | 11 | export default getTokenFromQuery; 12 | -------------------------------------------------------------------------------- /src/features/Onboarding/NavContainer.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const NavContainer = styled.div` 4 | display: flex; 5 | justify-content: space-around; 6 | width: 100%; 7 | 8 | @media only screen and (min-width: ${(props) => props.theme.screens.smUp}) { 9 | width: 50%; 10 | } 11 | `; 12 | 13 | export default NavContainer; 14 | -------------------------------------------------------------------------------- /src/assets/images/sidebar-app.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/shared/Form/StyledNumberFormat.tsx: -------------------------------------------------------------------------------- 1 | import { NumericFormat, PatternFormat } from 'react-number-format'; 2 | import styled from 'styled-components'; 3 | import inputStyles from '../Styles/inputStyles'; 4 | 5 | export const StyledPatternFormat = styled(PatternFormat)` 6 | ${inputStyles}; 7 | `; 8 | 9 | export const StyledNumericFormat = styled(NumericFormat)` 10 | ${inputStyles}; 11 | `; 12 | -------------------------------------------------------------------------------- /src/features/Nav/Links.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Links = styled.div` 4 | padding: 0 40px 0 0; 5 | align-items: center; 6 | position: fixed; 7 | top: 25px; 8 | right: 0; 9 | display: none; 10 | 11 | @media only screen and (min-width: ${(props) => props.theme.screens.smUp}) { 12 | display: flex; 13 | } 14 | `; 15 | 16 | export default Links; 17 | -------------------------------------------------------------------------------- /src/pages/Sponsor/Edit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { RouteProps } from 'react-router'; 3 | import ManageSponsor, { 4 | ManageSponsorModes, 5 | } from '../../features/Sponsor/SponsorProfileForm'; 6 | 7 | const EditSponsorPage: React.FC = (props: RouteProps) => { 8 | return ; 9 | }; 10 | 11 | export default EditSponsorPage; 12 | -------------------------------------------------------------------------------- /src/pages/Sponsor/Create.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { RouteProps } from 'react-router'; 3 | import ManageSponsor, { 4 | ManageSponsorModes, 5 | } from '../../features/Sponsor/SponsorProfileForm'; 6 | 7 | const CreateSponsorPage: React.FC = (props: RouteProps) => { 8 | return ; 9 | }; 10 | 11 | export default CreateSponsorPage; 12 | -------------------------------------------------------------------------------- /src/config/ethnicity.ts: -------------------------------------------------------------------------------- 1 | export enum IEthnicity { 2 | INDIAN_NATIVE = 'American Indian / Alaskan Native', 3 | ASIAN_PI = ' Asian / Pacific Islander', 4 | AFRO_AMER = 'Black / African American', 5 | INDIGENOUS_INUIT = 'Indigenous / Inuit', 6 | HISP = 'Hispanic', 7 | MID_EAST = 'Middle Eastern', 8 | EUROPEAN = 'White / Caucasian', 9 | NO_ANSWER = 'Prefer not to answer', 10 | } 11 | export default IEthnicity; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # debugging 10 | /.vscode/chrome 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /src/config/hackerStatus.ts: -------------------------------------------------------------------------------- 1 | export enum HackerStatus { 2 | HACKER_STATUS_NONE = 'None', 3 | HACKER_STATUS_APPLIED = 'Applied', 4 | HACKER_STATUS_ACCEPTED = 'Accepted', 5 | HACKER_STATUS_DECLINED = 'Declined', 6 | HACKER_STATUS_WAITLISTED = 'Waitlisted', 7 | HACKER_STATUS_CONFIRMED = 'Confirmed', 8 | HACKER_STATUS_WITHDRAWN = 'Withdrawn', 9 | HACKER_STATUS_CHECKED_IN = 'Checked-in', 10 | } 11 | export default HackerStatus; 12 | -------------------------------------------------------------------------------- /src/features/Login/ForgotPasswordLink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FrontendRoute } from '../../config'; 3 | import { LinkDuo } from '../../shared/Elements'; 4 | 5 | const ForgotPasswordLinkComponent: React.FC<{}> = (props) => { 6 | return ( 7 | 8 | Forgot password? 9 | 10 | ); 11 | }; 12 | 13 | export default ForgotPasswordLinkComponent; 14 | -------------------------------------------------------------------------------- /src/config/hackerReviewerStatus.ts: -------------------------------------------------------------------------------- 1 | export enum HackerReviewerStatus { 2 | HACKER_REVIEWER_STATUS_NONE = 'None', 3 | HACKER_REVIEWER_STATUS_POOR = 'Poor', 4 | HACKER_REVIEWER_STATUS_WEAK = 'Weak', 5 | HACKER_REVIEWER_STATUS_AVERAGE = 'Average', 6 | HACKER_REVIEWER_STATUS_STRONG = 'Strong', 7 | HACKER_REVIEWER_STATUS_OUTSTANDING = 'Outstanding', 8 | HACKER_REVIEWER_STATUS_WHITELIST = 'Whitelist', 9 | } 10 | export default HackerReviewerStatus; 11 | -------------------------------------------------------------------------------- /src/features/Onboarding/OnboardingNav.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Nav = styled.nav` 4 | z-index: 5; 5 | height: 40px; 6 | position: sticky; 7 | top: 0; 8 | left: 0; 9 | right: 0; 10 | padding-top: 50px; 11 | width: 100%; 12 | display: flex; 13 | justify-content: center; 14 | border-bottom: 2px solid transparent; 15 | transition: 0.25s border-color ease-in; 16 | `; 17 | 18 | export default Nav; 19 | -------------------------------------------------------------------------------- /src/config/travel.ts: -------------------------------------------------------------------------------- 1 | export interface ITravel { 2 | // The travel's id 3 | id: string; 4 | // The id of the user that is travelling 5 | accountId: string; 6 | // The id of the hacker that is travlling 7 | hackerId: string; 8 | // The status of the hacker 9 | status: string; 10 | // The amount of money the traveller is requesting 11 | request: number; 12 | // The amount of money we are offering the traveller 13 | offer: number; 14 | } 15 | -------------------------------------------------------------------------------- /src/pages/Travel/DollarAmount.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { H2 } from '../../shared/Elements'; 4 | 5 | const DollarAmount: React.FunctionComponent<{ amount: number }> = ({ 6 | amount, 7 | }) => ( 8 |

15 | ${amount.toFixed(2)} 16 |

17 | ); 18 | 19 | export default DollarAmount; 20 | -------------------------------------------------------------------------------- /src/assets/images/backarrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/shared/Elements/LinkDuo.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link, LinkProps } from 'react-router-dom'; 3 | 4 | export const LinkDuo: React.FC = (props: any) => { 5 | const { to, ...rest } = props; 6 | return /^https?:\/\//.test(to) ? ( 7 | 8 | {props.children} 9 | 10 | ) : ( 11 | 12 | {props.children} 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /.idea/dashboard.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/features/SingleHacker/SingleHackerField.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { Box } from '@rebass/grid'; 4 | 5 | interface IFieldProps { 6 | text: string | number | undefined; 7 | label: string; 8 | } 9 | 10 | const SingleHackerField: React.FunctionComponent = ({ 11 | text, 12 | label, 13 | }) => { 14 | return ( 15 | 16 | {label}: {text} 17 | 18 | ); 19 | }; 20 | 21 | export default SingleHackerField; 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"], 3 | "rulesDirectory": ["tslint-plugin-prettier"], 4 | "rules": { 5 | "prettier": true, 6 | "interface-name": true, 7 | "object-literal-sort-keys": false, 8 | "no-console": false, 9 | "object-literal-shorthand": true 10 | }, 11 | "linterOptions": { 12 | "exclude": [ 13 | "config/**/*.js", 14 | "node_modules/**/*.ts", 15 | "coverage/lcov-report/*.js" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/shared/Elements/Image.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export interface IImageProps { 4 | src: any; 5 | imgHeight?: string; 6 | imgWidth?: string; 7 | padding?: string; 8 | } 9 | 10 | export const Image = styled.img` 11 | src: ${(props: IImageProps) => props.src}; 12 | height: ${(props: IImageProps) => props.imgHeight || 'auto'}; 13 | width: ${(props: IImageProps) => props.imgWidth || 'auto'}; 14 | padding: ${(props: IImageProps) => props.padding || 0}; 15 | `; 16 | export default Image; 17 | -------------------------------------------------------------------------------- /src/shared/HOC/withToaster.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ToastContainer } from 'react-toastify'; 3 | 4 | const WithToaster =

(Component: React.ComponentType

) => 5 | class extends React.Component

{ 6 | public render() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | }; 15 | 16 | export default WithToaster; 17 | -------------------------------------------------------------------------------- /src/pages/Travel/TravelStatusNone.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { ITravel } from '../../config'; 4 | 5 | interface ITravelStatusProps { 6 | travel: ITravel; 7 | } 8 | 9 | const TravelStatusNone: React.FunctionComponent = ({ 10 | travel, 11 | }) => { 12 | return ( 13 |

14 | Your request to recieve ${travel.request.toFixed(2)} in reimbursement for 15 | travel is still being processed. 16 |
17 | ); 18 | }; 19 | 20 | export default TravelStatusNone; 21 | -------------------------------------------------------------------------------- /src/config/gradYears.ts: -------------------------------------------------------------------------------- 1 | // const GradYearList = [2020, 2021, 2022, 2023, 2024, 2025, 2026]; 2 | const presentDate = new Date(); 3 | const presentYear = presentDate.getFullYear(); 4 | const GradYearList = [ 5 | presentYear, 6 | presentYear + 1, 7 | presentYear + 2, 8 | presentYear + 3, 9 | presentYear + 4, 10 | presentYear + 5, 11 | presentYear + 6, 12 | presentYear + 7, 13 | presentYear + 8, 14 | presentYear + 9, 15 | presentYear + 10, 16 | ]; 17 | export const GradYears = GradYearList.map((v) => ({ label: v, value: v })); 18 | -------------------------------------------------------------------------------- /src/shared/Form/SubmitBtn.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Flex } from '@rebass/grid'; 2 | import * as React from 'react'; 3 | 4 | import { Button, ButtonVariant, IButtonProps } from '../Elements'; 5 | 6 | export const SubmitBtn: React.FC< 7 | IButtonProps & React.ButtonHTMLAttributes 8 | > = (props) => ( 9 | 10 | 11 | 14 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/features/Onboarding/Content.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Content = styled.div` 4 | display: flex; 5 | justify-content: space-between; 6 | max-width: 1200px; 7 | margin: auto; 8 | @media only screen and (max-width: 1345px) { 9 | max-width: 1000px; 10 | } 11 | @media only screen and (max-width: 1118px) { 12 | flex-direction: column; 13 | justify-content: center; 14 | padding-left: 30px; 15 | padding-right: 30px; 16 | padding-top: 40px; 17 | } 18 | `; 19 | 20 | export default Content; 21 | -------------------------------------------------------------------------------- /src/pages/Travel/TravelStatusClaimed.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { ITravel } from '../../config'; 4 | import DollarAmount from './DollarAmount'; 5 | 6 | interface ITravelStatusProps { 7 | travel: ITravel; 8 | } 9 | 10 | const TravelStatusClaimed: React.FunctionComponent = ({ 11 | travel, 12 | }) => { 13 | return ( 14 |
15 | We reimbursed you for which you 16 | have already claimed. 17 |
18 | ); 19 | }; 20 | 21 | export default TravelStatusClaimed; 22 | -------------------------------------------------------------------------------- /src/features/Onboarding/Text.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Text = styled.div` 4 | margin-bottom: 1rem; 5 | padding: 26px; 6 | h2 { 7 | color: #f2463a; 8 | font-size: 2rem; 9 | font-weight: 400; 10 | margin-bottom: 0.22rem; 11 | 12 | @media only screen and (min-width: ${(props) => props.theme.screens.smUp}) { 13 | font-size: 3rem; 14 | } 15 | } 16 | b { 17 | color: #f2463a; 18 | } 19 | a { 20 | :hover { 21 | color: #f2463a; 22 | } 23 | } 24 | `; 25 | 26 | export default Text; 27 | -------------------------------------------------------------------------------- /src/shared/Elements/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BackgroundImage'; 2 | export * from './Button'; 3 | export * from './Card'; 4 | export * from './ConfirmModal'; 5 | export * from './H1'; 6 | export * from './H2'; 7 | export * from './Image'; 8 | export * from './LeftContainer'; 9 | export * from './MaxWidthBox'; 10 | export * from './Paragraph'; 11 | export * from './StyledModal'; 12 | export * from './StyledModalSmall'; 13 | export * from './Table'; 14 | export * from './Textarea'; 15 | export * from './ViewPDF'; 16 | export * from './LinkDuo'; 17 | export * from './HorizontalSpacer'; 18 | -------------------------------------------------------------------------------- /src/assets/images/fb-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/features/Login/SignUpLink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FrontendRoute } from '../../config'; 3 | import { LinkDuo } from '../../shared/Elements'; 4 | import theme from '../../shared/Styles/theme'; 5 | 6 | const SignUpLink: React.FC<{}> = (props) => { 7 | return ( 8 |
9 | Don't have an account?   10 | 11 | Sign up 12 | 13 |
14 | ); 15 | }; 16 | 17 | export default SignUpLink; 18 | -------------------------------------------------------------------------------- /src/config/validationError.ts: -------------------------------------------------------------------------------- 1 | export interface IValidationError { 2 | status: number; 3 | message: string; 4 | data: { 5 | [id: string]: IValidationErrorItem; 6 | }; 7 | } 8 | 9 | export interface IValidationErrorItem { 10 | location: string; 11 | param: string; 12 | msg: string; 13 | } 14 | 15 | export function instanceOfIValidationErrorItem( 16 | object: any 17 | ): object is IValidationErrorItem { 18 | if (typeof object === 'object') { 19 | return 'location' in object && 'param' in object && 'msg' in object; 20 | } else { 21 | return false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/Application/Edit.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Helmet from 'react-helmet'; 3 | 4 | import ManageApplicationForm, { 5 | ManageApplicationModes, 6 | } from '../../features/Application/ManageApplicationForm'; 7 | 8 | import * as CONSTANTS from '../../config/constants'; 9 | 10 | const EditApplicationPage: React.FC = () => ( 11 | <> 12 | 13 | Edit Application | {CONSTANTS.HACKATHON_NAME} 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | export default EditApplicationPage; 21 | -------------------------------------------------------------------------------- /src/shared/Elements/HorizontalSpacer.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface IHorizontalSpacerProps { 4 | paddingLeft?: string; 5 | paddingRight?: string; 6 | } 7 | 8 | // A div with with some horizontal padding 9 | // --- 10 | // Example use case: To indent forms so that they can be centered in space not 11 | // filled by sidebar 12 | export const HorizontalSpacer = styled.div` 13 | padding-left: ${(props) => props.paddingLeft || '0'}; 14 | padding-right: ${(props) => props.paddingRight || '0'}; 15 | `; 16 | 17 | export default HorizontalSpacer; 18 | -------------------------------------------------------------------------------- /src/features/Account/AlreadyHaveAccount.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FrontendRoute } from '../../config'; 3 | import { LinkDuo } from '../../shared/Elements'; 4 | import theme from '../../shared/Styles/theme'; 5 | 6 | const AlreadyHaveAccount: React.FC<{}> = (props) => { 7 | return ( 8 |
9 | Already have an account?   10 | 11 | Sign in 12 | 13 |
14 | ); 15 | }; 16 | 17 | export default AlreadyHaveAccount; 18 | -------------------------------------------------------------------------------- /src/shared/Form/Label.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface ILabelProps { 4 | width?: string; 5 | fontWeight?: string; 6 | } 7 | 8 | export const Label = styled.label` 9 | font-family: ${(props) => props.theme.fonts.header}; 10 | font-weight: ${(props) => props.fontWeight || '400'}; 11 | color: ${(props) => props.theme.colors.black80}; 12 | display: block; 13 | width: ${(props) => props.width || '100%'}; 14 | 15 | p { 16 | font-size: 14px; 17 | color: ${(props) => props.theme.colors.black60}; 18 | } 19 | `; 20 | 21 | export default Label; 22 | -------------------------------------------------------------------------------- /src/pages/Application/Create.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Helmet from 'react-helmet'; 3 | 4 | import ManageApplicationForm, { 5 | ManageApplicationModes, 6 | } from '../../features/Application/ManageApplicationForm'; 7 | 8 | import * as CONSTANTS from '../../config/constants'; 9 | 10 | const CreateApplicationPage: React.FC = () => ( 11 | <> 12 | 13 | Create Application | {CONSTANTS.HACKATHON_NAME} 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | export default CreateApplicationPage; 21 | -------------------------------------------------------------------------------- /src/features/SingleHacker/SingleHackerParagraph.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { Box } from '@rebass/grid'; 4 | import { Paragraph } from '../../shared/Elements'; 5 | 6 | interface IParagraphProps { 7 | text: string | number | undefined; 8 | label: string; 9 | } 10 | 11 | const SingleHackerParagraph: React.FunctionComponent = ({ 12 | text, 13 | label, 14 | }) => { 15 | return ( 16 | 17 | {label}{' '} 18 | {text || 'N/A'} 19 | 20 | ); 21 | }; 22 | 23 | export default SingleHackerParagraph; 24 | -------------------------------------------------------------------------------- /src/features/Team/MemberList/MemberList.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { IMemberName } from '../../../config'; 4 | import MemberItem from './MemberItem'; 5 | 6 | interface IMemberListProps { 7 | members: IMemberName[]; 8 | } 9 | 10 | const MemberList: React.FC = (props) => ( 11 |
12 | {props.members.map((member, index) => ( 13 | 14 | ))} 15 | 16 | 22 |
23 | ); 24 | 25 | export default MemberList; 26 | -------------------------------------------------------------------------------- /src/features/Nav/LoginButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { FrontendRoute } from '../../config'; 3 | import { LinkDuo } from '../../shared/Elements'; 4 | import Button, { ButtonVariant } from '../../shared/Elements/Button'; 5 | 6 | const LoginBtn: React.FunctionComponent = () => { 7 | return ( 8 | 15 | 18 | 19 | ); 20 | }; 21 | 22 | export default LoginBtn; 23 | -------------------------------------------------------------------------------- /src/shared/HOC/withThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ThemeProvider } from 'styled-components'; 3 | import GlobalStyles from '../Styles/GlobalStyles'; 4 | import theme from '../Styles/theme'; 5 | 6 | const withThemeProvider =

(Component: React.ComponentType

) => 7 | class extends React.Component

{ 8 | public render() { 9 | return ( 10 | 11 |

12 | 13 | 14 |
15 | 16 | ); 17 | } 18 | }; 19 | 20 | export default withThemeProvider; 21 | -------------------------------------------------------------------------------- /src/features/Nav/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const NavLink = styled.a` 4 | font-family: 'Brown'; 5 | cursor: pointer; 6 | color: ${(props) => props.theme.colors.black60}; 7 | text-decoration: none; 8 | 9 | &:focus, 10 | &:hover, 11 | &:active { 12 | color: ${(props) => props.theme.colors.red}; 13 | background: transparent; 14 | } 15 | 16 | @media only screen and (min-width: ${(props) => props.theme.screens.smUp}) { 17 | margin-right: 2rem; 18 | margin-top: 1px; 19 | } 20 | 21 | &.active { 22 | color: ${(props) => props.theme.colors.red}; 23 | } 24 | `; 25 | 26 | export default NavLink; 27 | -------------------------------------------------------------------------------- /src/features/SingleHacker/SingleHackerSection.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { H2 } from '../../shared/Elements'; 3 | import theme from '../../shared/Styles/theme'; 4 | 5 | interface IProps { 6 | title: string; 7 | hidden?: boolean; 8 | children?: React.ReactNode; 9 | } 10 | 11 | const SingleHackerSection: React.FunctionComponent = ({ 12 | title, 13 | children, 14 | hidden, 15 | }) => { 16 | return hidden ? ( 17 |
18 | ) : ( 19 |
20 |
21 |

{title}

22 | {children} 23 |
24 | ); 25 | }; 26 | 27 | export default SingleHackerSection; 28 | -------------------------------------------------------------------------------- /src/shared/Form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Autosuggest'; 2 | export * from './Checkbox'; 3 | export * from './EmailInput'; 4 | export * from './FileInput'; 5 | export * from './FileUpload'; 6 | export * from './Form'; 7 | export * from './Input'; 8 | export * from './Label'; 9 | export * from './LabelText'; 10 | export * from './NumberFormatInput'; 11 | export * from './PasswordInput'; 12 | export * from './RequiredInputLabel'; 13 | export * from './SecondaryInfoText'; 14 | export * from './StyledCreatableSelect'; 15 | export * from './StyledNumberFormat'; 16 | export * from './StyledSelect'; 17 | export * from './SubmitBtn'; 18 | export * from './validationErrorGenerator'; 19 | -------------------------------------------------------------------------------- /src/features/HackPass/Pass.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { IAccount, IHacker } from '../../config'; 4 | 5 | interface IPassProps { 6 | account: IAccount; 7 | hacker: IHacker; 8 | qrData: string; 9 | } 10 | 11 | export const Pass: React.FunctionComponent = ( 12 | props: IPassProps 13 | ) => { 14 | return ( 15 |
16 | 17 |
18 |

{props.account.firstName}

19 |

{props.account.pronoun}

20 |

{props.hacker.application.general.school}

21 |
22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /src/shared/HOC/withBackground.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import theme from '../../shared/Styles/theme'; 4 | 5 | const withBackground =

(Component: React.ComponentType

) => { 6 | return (props: P) => { 7 | return ( 8 |

9 | 10 |
11 | ); 12 | }; 13 | }; 14 | 15 | const styles = { 16 | background: { 17 | width: '100%', 18 | height: '100%', 19 | minHeight: '100vh', 20 | background: `linear-gradient(to bottom, ${theme.colors.white}, ${theme.colors.black5} 100%)`, 21 | zIndex: -1001, 22 | }, 23 | }; 24 | 25 | export default withBackground; 26 | -------------------------------------------------------------------------------- /src/config/degrees.ts: -------------------------------------------------------------------------------- 1 | export enum Degrees { 2 | LESS_THAN_SECONDARY = 'Less than Secondary / High School', 3 | SECONDARY = 'Secondary / High School', 4 | CEGEP = 'CEGEP', 5 | COLLEGE = 'Undergraduate University (2 year - community college or similar)', 6 | UNDERGRADUATE = 'Undergraduate University (3+ year)', 7 | GRADUATE = 'Graduate University (Masters, Professional, Doctoral, etc)', 8 | CODE_SCHOOL = 'Code School / Bootcamp', 9 | VOCATIONAL = 'Other Vocational / Trade Program or Apprenticeship', 10 | POSTDOC = 'Post Doctorate', 11 | OTHER = 'Other', 12 | NOT_STUDENT = "I'm not currently a student", 13 | NO_ANSWER = 'Prefer not to answer', 14 | } 15 | export default Degrees; 16 | -------------------------------------------------------------------------------- /src/features/Nav/Nav.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface INavProps { 4 | hasBorder: boolean; 5 | } 6 | 7 | export const Nav = styled.nav` 8 | z-index: 10; 9 | height: 90px; 10 | position: sticky; 11 | top: 0; 12 | left: 0; 13 | right: 0; 14 | width: 100%; 15 | display: flex; 16 | justify-content: space-between; 17 | background: ${(props) => 18 | props.hasBorder ? props.theme.colors.white : 'transparent'}; 19 | border-bottom: 2px solid transparent; 20 | border-color: ${(props) => 21 | props.hasBorder ? props.theme.colors.black5 : 'transparent'}; 22 | transition: 0.25s border-color ease-in; 23 | `; 24 | 25 | export default Nav; 26 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Chrome", 9 | "type": "chrome", 10 | "request": "launch", 11 | "url": "http://localhost:1337", 12 | "webRoot": "${workspaceRoot}/src", 13 | "userDataDir": "${workspaceRoot}/.vscode/chrome", 14 | "sourceMapPathOverrides": { 15 | "webpack:///src/*": "${webRoot}/*" 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/api/search.ts: -------------------------------------------------------------------------------- 1 | import { AxiosPromise } from 'axios'; 2 | import { APIRoute, ISearchOptions, ISearchParameter } from '../config'; 3 | import API from './api'; 4 | import APIResponse from './APIResponse'; 5 | class SearchAPI { 6 | constructor() { 7 | API.createEntity(APIRoute.SEARCH); 8 | } 9 | public search( 10 | model: string, 11 | parameters: ISearchParameter[], 12 | searchOptions: ISearchOptions 13 | ): AxiosPromise> { 14 | return API.getEndpoint(APIRoute.SEARCH).getAll({ 15 | params: { 16 | q: JSON.stringify(parameters), 17 | model, 18 | ...searchOptions, 19 | }, 20 | }); 21 | } 22 | } 23 | export const Search = new SearchAPI(); 24 | export default Search; 25 | -------------------------------------------------------------------------------- /src/assets/images/passwordReset.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "allowJs": true, 5 | "skipLibCheck": false, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "noEmit": true, 14 | "jsx": "preserve", 15 | "lib": [ 16 | "es2015", 17 | "dom" 18 | ], 19 | "alwaysStrict": false, 20 | "strictFunctionTypes": false, 21 | "strictPropertyInitialization": false, 22 | "strict": true, 23 | "noImplicitReturns": true, 24 | "noUnusedLocals": false, 25 | }, 26 | "include": [ 27 | "src" 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/images/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/features/Onboarding/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const NavLink = styled.a` 4 | font-family: 'Brown'; 5 | cursor: pointer; 6 | color: ${(props) => props.theme.colors.black60}; 7 | text-decoration: none; 8 | font-size: 0.8rem; 9 | 10 | &:focus, 11 | &:hover, 12 | &:active { 13 | color: ${(props) => props.theme.colors.red}; 14 | background: transparent; 15 | } 16 | 17 | @media only screen and (min-width: ${(props) => props.theme.screens.smUp}) { 18 | margin-right: 2rem; 19 | margin-top: 1px; 20 | font-size: 1rem; 21 | } 22 | 23 | &.active { 24 | border-bottom: 2px solid ${(props) => props.theme.colors.red}; 25 | color: ${(props) => props.theme.colors.red}; 26 | } 27 | `; 28 | 29 | export default NavLink; 30 | -------------------------------------------------------------------------------- /src/shared/Elements/GridTwoColumn.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export interface IGridTwoColumnProps { 4 | columnWidth?: string; 5 | columnGap?: string; 6 | rowGap?: string; 7 | margin?: string; 8 | } 9 | 10 | export const GridTwoColumn = styled.div` 11 | display: grid; 12 | grid-template-columns: repeat( 13 | auto-fill, 14 | minmax( 15 | min( 16 | 80vw, 17 | ${(props: IGridTwoColumnProps) => props.columnWidth || '400px'} 18 | ), 19 | 1fr 20 | ) 21 | ); 22 | column-gap: ${(props: IGridTwoColumnProps) => props.columnGap || '80px'}; 23 | row-gap: ${(props: IGridTwoColumnProps) => props.rowGap || '20px'}; 24 | margin: ${(props: IGridTwoColumnProps) => props.margin || '0 0 60px 0'}; 25 | `; 26 | 27 | export default GridTwoColumn; 28 | -------------------------------------------------------------------------------- /src/assets/images/sidebar-profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/pages/Travel/TravelStatusPolicy.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { ITravel, TRAVEL_POLICY } from '../../config'; 4 | import { Button, LinkDuo } from '../../shared/Elements'; 5 | 6 | interface ITravelStatusProps { 7 | travel: ITravel; 8 | } 9 | 10 | const TravelStatusPolicy: React.FunctionComponent = () => { 11 | return ( 12 |
13 | Your travel reimbursement decision has been released. In order to see how 14 | much you will be reimbursed, you must first agree to our{' '} 15 | travel policy. 16 |
17 | 18 |
19 |
20 | ); 21 | }; 22 | 23 | export default TravelStatusPolicy; 24 | -------------------------------------------------------------------------------- /src/assets/images/sidebar-bus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/shared/Elements/H2.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface IH2Props { 4 | color?: string; 5 | fontSize?: string; 6 | fontWeight?: string; 7 | textAlign?: string; 8 | marginLeft?: string; 9 | marginTop?: string; 10 | marginBottom?: string; 11 | display?: string; 12 | } 13 | 14 | export const H2 = styled.h2` 15 | font-size: ${(props) => props.fontSize || '24px'}; 16 | text-align: ${(props) => props.textAlign || 'left'}; 17 | color: ${(props) => props.color || props.theme.colors.red}; 18 | font-weight: ${(props) => props.fontWeight || 'normal'}; 19 | margin-left: ${(props) => props.marginLeft || 'initial'}; 20 | margin-bottom: ${(props) => props.marginBottom || '12px'}; 21 | margin-top: ${(props) => props.marginTop || 'initial'}; 22 | display: ${(props) => props.display || ''}; 23 | `; 24 | 25 | export default H2; 26 | -------------------------------------------------------------------------------- /.idea/mongoSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /src/shared/Form/NumberFormatInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { NumberFormatValues, PatternFormatProps } from 'react-number-format'; 3 | import { Label, LabelText, StyledNumericFormat, StyledPatternFormat } from '.'; 4 | 5 | interface ILabelledNumberFormatProp { 6 | value?: string; 7 | onValueChange: (value: NumberFormatValues) => void; 8 | label: string; 9 | placeholder: string; 10 | required?: boolean; 11 | disabled?: boolean; 12 | } 13 | export const NumberFormatInput: React.FunctionComponent< 14 | ILabelledNumberFormatProp & PatternFormatProps 15 | > = (props) => { 16 | return ( 17 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /src/shared/HOC/withNavbar.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@rebass/grid'; 2 | import * as React from 'react'; 3 | import Navbar from '../../features/Nav/Navbar'; 4 | 5 | export interface INavbarOptions { 6 | // The active page 7 | activePage: string; 8 | } 9 | 10 | const defaultOptions = { 11 | activePage: 'home', 12 | }; 13 | 14 | const withNavbar =

( 15 | Component: React.ComponentType

, 16 | options: INavbarOptions = defaultOptions 17 | ) => 18 | class extends React.Component

{ 19 | public render() { 20 | return ( 21 | 25 | 26 | 27 | 28 | ); 29 | } 30 | }; 31 | 32 | export default withNavbar; 33 | -------------------------------------------------------------------------------- /src/features/Invite/ExistingInvitesTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { IInviteInfo } from '../../config'; 3 | import { StyledTable } from '../../shared/Elements'; 4 | 5 | interface IExistingInvitesTableProps { 6 | // Optional function that is called when a new invite is sent. 7 | invites: IInviteInfo[]; 8 | isLoading: boolean; 9 | } 10 | 11 | export const ExistingInvitesTable: React.FC = ( 12 | props 13 | ) => { 14 | const columns = [ 15 | { 16 | Header: 'Email', 17 | accessor: 'email', 18 | }, 19 | { 20 | Header: 'Account Type', 21 | accessor: 'accountType', 22 | }, 23 | ]; 24 | return ( 25 | 31 | ); 32 | }; 33 | 34 | export default ExistingInvitesTable; 35 | -------------------------------------------------------------------------------- /docs/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Tickets: 2 | 3 | - HCK- 4 | 5 | ### List of changes: 6 | 7 | - 8 | 9 | ### Type of change: 10 | 11 | Please delete options that aren't relevant. 12 | 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 16 | - [ ] This change requires a documentation update 17 | 18 | ### How did you do this? 19 | 20 | ### How to test: 21 | 22 | ### Questions: 23 | 24 | ### PR Checklist: 25 | 26 | - [ ] Merged `develop` branch (before testing) 27 | - [ ] Linted my code locally 28 | - [ ] Listed change(s) in the Changelog 29 | - [ ] Tested all links in project relevant browsers 30 | - [ ] Tested all links on different screen sizes 31 | - [ ] Referenced all useful info (issues, tasks, etc) 32 | 33 | ### Screenshots: 34 | -------------------------------------------------------------------------------- /src/features/SingleHacker/SingleHackerLink.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { Box } from '@rebass/grid'; 4 | 5 | interface ILinkProps { 6 | link?: string; 7 | linkText?: string; 8 | label: string; 9 | } 10 | 11 | const SingleHackerLink: React.FunctionComponent = ({ 12 | link, 13 | linkText, 14 | label, 15 | }) => { 16 | if (link) { 17 | const url = new URL(link); 18 | const target = 19 | ['https:', 'http:'].indexOf(url.protocol) !== -1 ? '_blank' : ''; 20 | return ( 21 | 22 | {label}:{' '} 23 | 24 | {linkText ? linkText : link} 25 | 26 | 27 | ); 28 | } else { 29 | return ( 30 | 31 | {label}: None 32 | 33 | ); 34 | } 35 | }; 36 | 37 | export default SingleHackerLink; 38 | -------------------------------------------------------------------------------- /src/shared/Elements/MaxWidthBox.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@rebass/grid'; 2 | import styled from 'styled-components'; 3 | 4 | export interface IMaxWidthBoxProps { 5 | maxWidth?: string; 6 | textAlign?: string; 7 | paddingLeft?: string; 8 | paddingRight?: string; 9 | float?: string; 10 | minWidth?: string; 11 | left?: string; 12 | position?: string; 13 | right?: string; 14 | } 15 | 16 | export const MaxWidthBox = styled(Box)` 17 | max-width: ${(props) => props.maxWidth || '600px'}; 18 | text-align: ${(props) => props.textAlign || 'initial'}; 19 | padding-left: ${(props) => props.paddingLeft || '0'}; 20 | padding-right: ${(props) => props.paddingRight || '0'}; 21 | float: ${(props) => props.float || 'none'}; 22 | left: ${(props) => props.left || 'auto'}; 23 | right: ${(props) => props.right || 'auto'}; 24 | position: ${(props) => props.position || 'static'}; 25 | `; 26 | 27 | export default MaxWidthBox; 28 | -------------------------------------------------------------------------------- /src/config/reviewers.ts: -------------------------------------------------------------------------------- 1 | export const reviewers = [ 2 | '', 3 | 'Aditi', 4 | 'Amy', 5 | 'Carolyn', 6 | 'Clara', 7 | 'Debo', 8 | 'Deon', 9 | 'Doaa', 10 | 'Emily', 11 | 'Emma', 12 | 'Ethan', 13 | 'Evan', 14 | 'Finnley', 15 | 'Gabriel', 16 | 'Ian', 17 | 'Inaya', 18 | 'Jake', 19 | 'Jamie', 20 | 'Jane J.', 21 | 'Jane K.', 22 | 'Jeffrey', 23 | 'Joshua', 24 | 'Jyothsna', 25 | 'Khyati', 26 | 'Michael', 27 | 'Mika', 28 | 'Mubeen', 29 | 'Mira', 30 | 'Oishika', 31 | 'Olivia', 32 | 'Qi', 33 | 'Rémi', 34 | 'Sebastian', 35 | 'Shirley', 36 | 'Sihan', 37 | 'Siva', 38 | 'Snigdha', 39 | 'Sonia', 40 | 'Stephanie', 41 | 'Tavi', 42 | 'Tina', 43 | 'Vipul', 44 | 'Yue Qian', 45 | ]; 46 | 47 | export const reviewerOptions = reviewers.map((reviewer) => ({ 48 | label: reviewer, 49 | value: reviewer, 50 | })); 51 | -------------------------------------------------------------------------------- /src/config/searchParameter.ts: -------------------------------------------------------------------------------- 1 | export enum StringOperations { 2 | EQUALS = 'equals', 3 | NOT_EQUALS = 'ne', 4 | REGEXP = 'regex', 5 | IN = 'in', 6 | } 7 | export enum NumberOperations { 8 | EQUALS = 'equals', 9 | NOT_EQUALS = 'ne', 10 | GREATER_THAN_OR_EQUAL = 'gte', 11 | LESS_THAN_OR_EQUAL = 'lte', 12 | GREATER_THAN = 'gt', 13 | LESS_THAN = 'lt', 14 | IN = 'in', 15 | } 16 | export enum BooleanOperations { 17 | EQUALS = 'equals', 18 | NOT_EQUALS = 'ne', 19 | } 20 | export interface ISearchParameter { 21 | param: string; 22 | operation: StringOperations | NumberOperations | BooleanOperations; 23 | value: string | boolean | number | string[] | number[]; 24 | } 25 | 26 | export function isValidSearchParameter(parameter: any) { 27 | if (!parameter) { 28 | return false; 29 | } 30 | const { param, operation, value } = parameter; 31 | if (param && typeof param === 'string' && operation && value) { 32 | return true; 33 | } 34 | return false; 35 | } 36 | -------------------------------------------------------------------------------- /src/shared/Styles/style.d.ts: -------------------------------------------------------------------------------- 1 | import 'styled-components'; 2 | 3 | declare module 'styled-components' { 4 | export interface DefaultTheme { 5 | colors: { 6 | red: string; 7 | redMed: string; 8 | redLight: string; 9 | blue: string; 10 | blueLight: string; 11 | yellow: string; 12 | yellowLight: string; 13 | teal: string; 14 | tealLight: string; 15 | purple: string; 16 | purpleLight: string; 17 | greyLight: string; 18 | white: string; 19 | black: string; 20 | black80: string; 21 | black70: string; 22 | black60: string; 23 | black40: string; 24 | black30: string; 25 | black20: string; 26 | black10: string; 27 | black5: string; 28 | }; 29 | fonts: { 30 | header: string; 31 | body: string; 32 | }; 33 | inputBorderRadius: string; 34 | screens: { 35 | smUp: string; 36 | mdUp: string; 37 | lgUp: string; 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/shared/Form/FormikElements/Input.tsx: -------------------------------------------------------------------------------- 1 | import { FieldProps } from 'formik'; 2 | import React from 'react'; 3 | import { Input, Label, LabelText } from '..'; 4 | 5 | interface IInputFormikComponentProp { 6 | label: string; 7 | inputType: string; 8 | placeholder?: string; 9 | required?: boolean; 10 | disabled?: boolean; 11 | showOptionalLabel?: boolean; 12 | } 13 | const InputFormikComponent: React.FC< 14 | IInputFormikComponentProp & FieldProps 15 | > = ({ 16 | placeholder, 17 | label, 18 | required, 19 | inputType, 20 | field, 21 | disabled, 22 | showOptionalLabel, 23 | }) => { 24 | return ( 25 | 38 | ); 39 | }; 40 | 41 | export { InputFormikComponent as Input }; 42 | -------------------------------------------------------------------------------- /src/shared/HOC/withContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Sponsor } from '../../api'; 3 | import { ISponsor } from '../../config'; 4 | import NomineeContext from '../../features/Search/Context'; 5 | 6 | interface IState { 7 | sponsor?: ISponsor; 8 | } 9 | 10 | const withContext =

(Component: React.ComponentType

) => 11 | class extends React.Component { 12 | constructor(props: any) { 13 | super(props); 14 | this.state = { 15 | sponsor: undefined, 16 | }; 17 | } 18 | 19 | public async componentDidMount() { 20 | try { 21 | const sponsor = (await Sponsor.getSelf()).data.data; 22 | this.setState({ sponsor }); 23 | } catch (e) { 24 | return; 25 | } 26 | } 27 | 28 | public render() { 29 | return ( 30 | 31 | 32 | 33 | ); 34 | } 35 | }; 36 | 37 | export default withContext; 38 | -------------------------------------------------------------------------------- /src/assets/images/sidebar-team.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/shared/Elements/Card.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@rebass/grid'; 2 | import styled from 'styled-components'; 3 | 4 | interface ICardProps { 5 | disabled?: boolean; 6 | } 7 | 8 | export const Card = styled(Flex)` 9 | height: 300px; 10 | max-width: 250px; 11 | margin: 15px; 12 | background-color: ${(props) => props.theme.colors.black5}; 13 | position: relative; 14 | padding: 20px; 15 | box-shadow: 5px 5px 20px ${(props) => props.theme.colors.black30}; 16 | 17 | ${(props) => 18 | props.disabled 19 | ? ` 20 | cursor: not-allowed; 21 | filter: grayscale(100%); 22 | &:after { 23 | content: ''; 24 | width: 100%; 25 | height: 100%; 26 | position: absolute; 27 | background: rgba(0,0,0,0.1); 28 | top: 0; 29 | left: 0; 30 | } 31 | ` 32 | : ` 33 | &:hover { 34 | transform: translate(-2px, -2px); 35 | box-shadow: 5px 5px 20px ${props.theme.colors.black60}; 36 | transition: 0.1s all ease-in; 37 | } 38 | `} 39 | `; 40 | 41 | export default Card; 42 | -------------------------------------------------------------------------------- /src/assets/images/twitter-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/config/frontendRoutes.ts: -------------------------------------------------------------------------------- 1 | export enum FrontendRoute { 2 | ADMIN_SEARCH_PAGE = '/admin/search', 3 | CHECKIN_HACKER_PAGE = '/hacker/checkin', 4 | PASS_HACKER_PAGE = '/hacker/pass', 5 | CONFIRM_ACCOUNT_PAGE = '/account/confirm', 6 | CREATE_ACCOUNT_PAGE = '/account/create', 7 | CREATE_APPLICATION_PAGE = '/application/create', 8 | CONFIRM_HACKER_PAGE = '/application/confirm', 9 | VIEW_HACKER_PAGE = '/application/view/:id', 10 | EDIT_ACCOUNT_PAGE = '/account/edit', 11 | EDIT_APPLICATION_PAGE = '/application/edit', 12 | FORGOT_PASSWORD_PAGE = '/password/forgot', 13 | WELL_KNOWN_PASSWORD_CHANGE = '/.well-known/change-password', 14 | INVITE_PAGE = '/invite', 15 | HOME_PAGE = '/', 16 | LOGIN_PAGE = '/login', 17 | RESET_PASSWORD_PAGE = '/password/reset', 18 | SETTINGS_PAGE = '/settings', 19 | SPONSOR_SEARCH_PAGE = '/sponsor/search', 20 | SPONSOR_ONBOARDING_PAGE = '/sponsor/onboarding', 21 | TEAM_PAGE = '/team', 22 | // TRAVEL_PAGE = '/travel', 23 | 24 | CREATE_SPONSOR_PAGE = '/sponsor/create', 25 | EDIT_SPONSOR_PAGE = '/sponsor/edit', 26 | } 27 | 28 | export default FrontendRoute; 29 | -------------------------------------------------------------------------------- /src/shared/Elements/H1.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface IH1Props { 4 | color?: string; 5 | fontSize?: string; 6 | textAlign?: string; 7 | marginLeft?: string; 8 | marginTop?: string; 9 | marginBottom?: string; 10 | display?: string; 11 | fontWeight?: string; 12 | paddingTop?: string; 13 | paddingBottom?: string; 14 | paddingLeft?: string; 15 | } 16 | 17 | export const H1 = styled.h1` 18 | font-size: ${(props) => props.fontSize || '32px'}; 19 | font-faimly: ${(props) => props.theme.fonts.header}; 20 | text-align: ${(props) => props.textAlign || 'left'}; 21 | color: ${(props) => props.color || props.theme.colors.red}; 22 | margin-left: ${(props) => props.marginLeft || '0'}; 23 | margin-bottom: ${(props) => props.marginBottom || '64px'}; 24 | margin-top: ${(props) => props.marginTop || '0'}; 25 | display: ${(props) => props.display || ''}; 26 | font-weight: ${(props) => props.fontWeight || 'normal'}; 27 | padding-top: ${(props) => props.paddingTop || '0'}; 28 | padding-bottom: ${(props) => props.paddingBottom || '0'}; 29 | `; 30 | 31 | export default H1; 32 | -------------------------------------------------------------------------------- /src/assets/images/github-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 HackMcGill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './APIRoute'; 2 | export * from './attendanceOptions'; 3 | export * from './authToken'; 4 | export * from './constants'; 5 | export * from './countries'; 6 | export * from './degrees'; 7 | export * from './dietaryRestrictions'; 8 | export * from './ethnicity'; 9 | export * from './frontendRoutes'; 10 | export * from './genders'; 11 | export * from './gradYears'; 12 | export * from './hackerStatus'; 13 | export * from './hackerReviewerStatus'; 14 | export * from './inviteInfo'; 15 | export * from './jobInterests'; 16 | export * from './majors'; 17 | export * from './pronouns'; 18 | export * from './resumeResponse'; 19 | export * from './schools'; 20 | export * from './searchOptions'; 21 | export * from './searchParameter'; 22 | export * from './settings'; 23 | export * from './shirtSizes'; 24 | export * from './skills'; 25 | export * from './previousHackathons'; 26 | export * from './statsResponse'; 27 | export * from './team'; 28 | export * from './travelStatus'; 29 | export * from './travel'; 30 | export * from './userTypes'; 31 | export * from './validationError'; 32 | export * from './pageType'; 33 | export * from './reviewers' 34 | -------------------------------------------------------------------------------- /src/shared/Elements/StyledModal.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import Modal from 'react-modal'; 5 | 6 | const ReactModalAdapter = ({ className, ...props }: any) => { 7 | const contentClassName = `${className}__content`; 8 | const overlayClassName = `${className}__overlay`; 9 | return ( 10 | 16 | ); 17 | }; 18 | 19 | export const StyledModal = styled(ReactModalAdapter)` 20 | &__overlay { 21 | position: fixed; 22 | top: 0px; 23 | left: 0px; 24 | right: 0px; 25 | bottom: 0px; 26 | background-color: rgba(0, 0, 0, 0.5); 27 | z-index: 20; 28 | } 29 | 30 | &__content { 31 | position: absolute; 32 | top: 40px; 33 | left: 40px; 34 | right: 40px; 35 | bottom: 40px; 36 | background: #fff; 37 | overflow: auto; 38 | -webkit-overflow-scrolling: touch; 39 | border-radius: 4px; 40 | outline: none; 41 | padding: 20px; 42 | } 43 | `; 44 | 45 | export default StyledModal; 46 | -------------------------------------------------------------------------------- /src/features/Invite/ExistingInvites.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Account } from '../../api'; 3 | import { IInviteInfo } from '../../config'; 4 | import ValidationErrorGenerator from '../../shared/Form/validationErrorGenerator'; 5 | import { ExistingInvitesTable } from './ExistingInvitesTable'; 6 | 7 | export const ExistingInvites: React.FC = () => { 8 | // Are we fetching the invites? 9 | const [isLoading, setIsLoading] = useState(true); 10 | // Invites array 11 | const [invites, setInvites] = useState([]); 12 | 13 | useEffect(() => { 14 | (async () => { 15 | // Load invites 16 | try { 17 | const result = await Account.getInvites(); 18 | const allInvites = result.data.data.invites; 19 | console.log(allInvites); 20 | setInvites(allInvites); 21 | } catch (e: any) { 22 | if (e && e.data) { 23 | ValidationErrorGenerator(e); 24 | } 25 | } 26 | setIsLoading(false); 27 | })(); 28 | }, []); 29 | return ; 30 | }; 31 | 32 | export default ExistingInvites; 33 | -------------------------------------------------------------------------------- /src/pages/Admin/Invite.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Helmet from 'react-helmet'; 3 | 4 | import { HACKATHON_NAME } from '../../config/constants'; 5 | import ExistingInvites from '../../features/Invite/ExistingInvites'; 6 | import InviteForm from '../../features/Invite/InviteForm'; 7 | import { H1, MaxWidthBox } from '../../shared/Elements'; 8 | import GridTwoColumn from '../../shared/Elements/GridTwoColumn'; 9 | import WithToaster from '../../shared/HOC/withToaster'; 10 | 11 | const InvitePage: React.FC = () => ( 12 | <> 13 | 14 | Invite User | {HACKATHON_NAME} 15 | 16 | 17 |

26 | User Invites 27 |

28 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | 36 | export default WithToaster(InvitePage); 37 | -------------------------------------------------------------------------------- /src/pages/Travel/TravelStatusBus.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { 4 | BUS_DEPARTURE_CITY, 5 | BUS_SHOPIFY_PAGE, 6 | BUS_SLACK_CHANNEL, 7 | BUS_SLACK_PAGE, 8 | HACKATHON_NAME, 9 | ITravel, 10 | } from '../../config'; 11 | import { LinkDuo } from '../../shared/Elements'; 12 | 13 | interface ITravelStatusProps { 14 | travel: ITravel; 15 | } 16 | 17 | const TravelStatusBus: React.FunctionComponent = ({ 18 | travel, 19 | }) => { 20 | return ( 21 |
22 | Congratulations, you've secured a seat on our {BUS_DEPARTURE_CITY} bus{' '} 23 | to/from {HACKATHON_NAME}! 24 |
25 |
26 |

Bus

27 | Join the {BUS_SLACK_CHANNEL} on our official{' '} 28 | Slack for details and more 29 | information about your bus route. 30 |
31 |
32 | If you can no longer make it to McHacks, please{' '} 33 | contact 34 | us so we can refund your deposit and open the seat up to another hacker. 35 |
36 | ); 37 | }; 38 | 39 | export default TravelStatusBus; 40 | -------------------------------------------------------------------------------- /src/shared/Elements/StyledModalSmall.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import Modal from 'react-modal'; 5 | 6 | const ReactModalAdapter = ({ className, ...props }: any) => { 7 | const contentClassName = `${className}__content`; 8 | const overlayClassName = `${className}__overlay`; 9 | return ( 10 | 16 | ); 17 | }; 18 | 19 | export const StyledModalSmall = styled(ReactModalAdapter)` 20 | &__overlay { 21 | display: flex; 22 | flex-direction: column; 23 | justify-content: center; 24 | align-items: center; 25 | position: fixed; 26 | top: 0px; 27 | left: 0px; 28 | right: 0px; 29 | bottom: 0px; 30 | background-color: rgba(0, 0, 0, 0.5); 31 | z-index: 20; 32 | } 33 | 34 | &__content { 35 | background: #fff; 36 | overflow: auto; 37 | -webkit-overflow-scrolling: touch; 38 | border-radius: 4px; 39 | outline: none; 40 | padding: 20px; 41 | } 42 | `; 43 | 44 | export default StyledModalSmall; 45 | -------------------------------------------------------------------------------- /src/features/Application/PaginationHeader/SeperatingBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import theme from '../../../shared/Styles/theme'; 3 | 4 | interface ISeparatingBarProps { 5 | current: boolean; 6 | totalPages?: number; 7 | } 8 | /** 9 | * Dividing bar between NumberBubble components 10 | */ 11 | const SeparatingBar: React.FC = (props) => { 12 | const pageNotSelectedBarStyle = { 13 | background: theme.colors.black40, 14 | width: props.totalPages 15 | ? `min(calc(calc(100% - ${props.totalPages * 24}px) / ${ 16 | props.totalPages - 1 17 | }), 160px)` 18 | : '160px', 19 | height: '2px', 20 | textAlign: 'center' as 'center', 21 | verticalAlign: 'center', 22 | }; 23 | 24 | const pageSelectedBarStyle = { 25 | ...pageNotSelectedBarStyle, 26 | background: theme.colors.purple, 27 | boxShadow: `2px 2px 16px 2px ${theme.colors.blueLight}`, 28 | }; 29 | 30 | const barStyle = props.current // The bar before the current page will be purple 31 | ? pageSelectedBarStyle 32 | : pageNotSelectedBarStyle; 33 | 34 | return
; 35 | }; 36 | 37 | export default SeparatingBar; 38 | -------------------------------------------------------------------------------- /src/shared/Elements/TextButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import theme from '../Styles/theme'; 3 | 4 | export interface ITextButtonProps { 5 | isGrey?: boolean; 6 | isLoading?: boolean; 7 | onClick?: any; 8 | children?: React.ReactNode; 9 | } 10 | 11 | /** 12 | * Button that displays simmilarly to a link 13 | */ 14 | const TextButton: React.FC = (props) => ( 15 | <> 16 | {props.isLoading ? ( 17 |
Loading...
18 | ) : ( 19 |
20 | {props.children} 21 |
22 | )} 23 | 40 | 41 | ); 42 | 43 | export default TextButton; 44 | -------------------------------------------------------------------------------- /src/features/Application/ResumeComponent.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@rebass/grid'; 2 | import { FieldProps } from 'formik'; 3 | import * as React from 'react'; 4 | import ViewPDFComponent from '../../shared/Elements/ViewPDF'; 5 | import { FileUpload, Label, LabelText } from '../../shared/Form'; 6 | import { ManageApplicationModes } from './ManageApplicationForm'; 7 | 8 | export interface IResumeProps { 9 | label: string; 10 | mode: ManageApplicationModes; 11 | hackerId: string; 12 | value?: boolean; 13 | required?: boolean; 14 | } 15 | const ResumeComponent: React.FC = (props) => { 16 | const viewResume = ; 17 | return ( 18 |
19 | 20 | 23 | 24 | {props.mode === ManageApplicationModes.EDIT ? ( 25 | 26 | {viewResume} 27 | 28 | ) : null} 29 | 30 |
31 | ); 32 | }; 33 | export { ResumeComponent }; 34 | export default ResumeComponent; 35 | -------------------------------------------------------------------------------- /src/api/emails.ts: -------------------------------------------------------------------------------- 1 | import { AxiosPromise } from 'axios'; 2 | import API from './api'; 3 | import APIResponse from './APIResponse'; 4 | import axios from 'axios'; 5 | 6 | class EmailsAPI { 7 | constructor() { 8 | API.createEntity('email'); 9 | } 10 | 11 | /** 12 | * Trigger automated status emails endpoint 13 | * POST /api/email/automated/status/:status 14 | */ 15 | public sendAutomatedStatus( 16 | status: string 17 | ): AxiosPromise> { 18 | return API.getEndpoint('email').create(undefined, { 19 | subURL: `automated/status/${status}`, 20 | config: { withCredentials: true }, 21 | }); 22 | } 23 | 24 | /** 25 | * Get count of hackers with specified status 26 | * GET /api/email/automated/status/:status/count 27 | */ 28 | public getStatusCount( 29 | status: string 30 | ): AxiosPromise> { 31 | const baseURL = API.getEndpoint('email')['resourceURL']; 32 | return axios.get(`${baseURL}/automated/status/${status}/count`, { 33 | withCredentials: true, 34 | }); 35 | } 36 | } 37 | 38 | export const Emails = new EmailsAPI(); 39 | export default Emails; 40 | -------------------------------------------------------------------------------- /src/api/settings.ts: -------------------------------------------------------------------------------- 1 | import { AxiosPromise, AxiosResponse } from 'axios'; 2 | import { APIResponse } from '.'; 3 | import { APIRoute, CACHE_SETTINGS_KEY, ISetting } from '../config'; 4 | import LocalCache from '../util/LocalCache'; 5 | import API from './api'; 6 | 7 | class SettingsAPI { 8 | constructor() { 9 | API.createEntity(APIRoute.SETTINGS); 10 | } 11 | /** 12 | * Update the settings. 13 | * @param setting The settings that you want to update 14 | */ 15 | public update(setting: ISetting): AxiosPromise { 16 | return API.getEndpoint(APIRoute.SETTINGS).patch({ id: '' }, setting); 17 | } 18 | /** 19 | * Get the current settings 20 | */ 21 | public async get( 22 | overrideCache?: boolean 23 | ): Promise>> { 24 | const cached: any = LocalCache.get(CACHE_SETTINGS_KEY); 25 | if (cached && !overrideCache) { 26 | return cached as AxiosResponse>; 27 | } 28 | const value = await API.getEndpoint(APIRoute.SETTINGS).getAll(); 29 | LocalCache.set(CACHE_SETTINGS_KEY, value); 30 | return value; 31 | } 32 | } 33 | 34 | export const Settings = new SettingsAPI(); 35 | export default Settings; 36 | -------------------------------------------------------------------------------- /src/shared/Form/StyledSelect.tsx: -------------------------------------------------------------------------------- 1 | import Select from 'react-select'; 2 | import styled from 'styled-components'; 3 | import inputStyles, { IInputProps } from '../Styles/inputStyles'; 4 | 5 | export const StyledSelect = styled(Select)` 6 | font-family: ${(props) => props.theme.fonts.body}; 7 | 8 | .react-select__control { 9 | ${inputStyles} 10 | display: flex; 11 | cursor: pointer; 12 | &--is-focused { 13 | box-shadow: 2px 2px 16px 0px 14 | ${(props: any) => props.theme.colors.blueLight}; 15 | } 16 | } 17 | 18 | .react-select__option { 19 | font-weight: normal; 20 | color: ${(props) => props.theme.colors.black70}; 21 | padding-left: 18px; 22 | cursor: pointer; 23 | &--is-selected { 24 | background-color: ${(props) => props.theme.colors.white}; 25 | color: ${(props) => props.theme.colors.black70}; 26 | } 27 | &--is-focused, 28 | &:hover { 29 | background-color: ${(props) => props.theme.colors.black10}; 30 | color: ${(props) => props.theme.colors.black70}; 31 | } 32 | } 33 | 34 | .react-select__value-container { 35 | padding-left: 0; 36 | } 37 | `; 38 | 39 | export default StyledSelect; 40 | -------------------------------------------------------------------------------- /src/features/Nav/LogoutButton.tsx: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios'; 2 | import React from 'react'; 3 | import { NavigateFunction, useNavigate } from 'react-router-dom'; 4 | import { APIResponse, Auth } from '../../api'; 5 | import { FrontendRoute, IValidationError } from '../../config'; 6 | import Button, { ButtonVariant } from '../../shared/Elements/Button'; 7 | import ValidationErrorGenerator from '../../shared/Form/validationErrorGenerator'; 8 | 9 | const LogoutBtn: React.FunctionComponent = () => { 10 | const navigate = useNavigate(); 11 | return ( 12 | 19 | ); 20 | }; 21 | 22 | function handleLogout(navigate: NavigateFunction): () => void { 23 | return () => { 24 | Auth.logout() 25 | .then(() => { 26 | navigate(FrontendRoute.LOGIN_PAGE); 27 | }) 28 | .catch((response: AxiosResponse>) => { 29 | if (response && response.data) { 30 | ValidationErrorGenerator(response.data); 31 | } 32 | }); 33 | }; 34 | } 35 | 36 | export default LogoutBtn; 37 | -------------------------------------------------------------------------------- /src/shared/Form/validationErrorGenerator.tsx: -------------------------------------------------------------------------------- 1 | import { toast } from 'react-toastify'; 2 | import 'react-toastify/dist/ReactToastify.css'; 3 | import { APIResponse } from '../../api'; 4 | import { 5 | instanceOfIValidationErrorItem, 6 | IValidationError, 7 | IValidationErrorItem, 8 | } from '../../config'; 9 | 10 | export default function ValidationErrorGenerator( 11 | response: APIResponse, 12 | autoClose: boolean = true 13 | ) { 14 | if (!response) { 15 | return; 16 | } 17 | const errors: any = response.data; 18 | toast.error(response.message, { 19 | position: toast.POSITION.TOP_RIGHT, 20 | autoClose: autoClose ? 5000 : false, 21 | }); 22 | 23 | for (const key in errors) { 24 | // check if the property/key is defined in the object itself, not in parent 25 | if ( 26 | errors.hasOwnProperty(key) && 27 | instanceOfIValidationErrorItem(errors[key]) 28 | ) { 29 | toast.error(validationErrorItem(key, errors[key]), { 30 | position: toast.POSITION.TOP_RIGHT, 31 | autoClose: false, 32 | }); 33 | } 34 | } 35 | } 36 | function validationErrorItem(key: string, errorItem: IValidationErrorItem) { 37 | return `Invalid ${key}: ${errorItem.msg}`; 38 | } 39 | -------------------------------------------------------------------------------- /src/assets/images/share-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/shared/Elements/Paragraph.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export interface IParagraphProps { 4 | fontSize?: string; 5 | textAlign?: string; 6 | italic?: boolean; 7 | paddingBottom?: string; 8 | maxWidth?: string; 9 | marginTop?: string; 10 | marginBottom?: string; 11 | marginLeft?: string; 12 | marginRight?: string; 13 | color?: string; 14 | } 15 | 16 | export const Paragraph = styled.p` 17 | ${(props) => props.italic && 'font-style: italic;'} 18 | font-size: ${(props) => props.fontSize || '24px'}; 19 | color: ${(props) => props.color || props.theme.colors.black80}; 20 | text-align: ${(props) => props.textAlign || 'left'}; 21 | padding-bottom: ${(props) => props.paddingBottom || '0px'}; 22 | max-width: ${(props) => props.maxWidth || '600px'}; 23 | margin-bottom: ${(props) => props.marginBottom || '18px'}; 24 | margin-left: ${(props) => props.marginLeft || 0}; 25 | margin-right: ${(props) => props.marginRight || 0}; 26 | margin-top: ${(props) => props.marginTop || '18px'}; 27 | `; 28 | 29 | export const FormDescription = styled(Paragraph)` 30 | font-size: 14px; 31 | margin-top: 4px; 32 | color: ${(props) => props.color || props.theme.colors.black60}; 33 | `; 34 | export default Paragraph; 35 | -------------------------------------------------------------------------------- /src/pages/Travel/TravelStatusOffered.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { ITravel, TRAVEL_RECEIPTS_FORM } from '../../config'; 4 | import { LinkDuo } from '../../shared/Elements'; 5 | import DollarAmount from './DollarAmount'; 6 | 7 | interface ITravelStatusProps { 8 | travel: ITravel; 9 | } 10 | 11 | const TravelStatusOffered: React.FunctionComponent = ({ 12 | travel, 13 | }) => { 14 | if (travel.offer > 0) { 15 | return ( 16 |
17 | We're happy to offer an amount to subsidize your travel to McHacks. We 18 | can reimburse you up to: 19 | 20 |
27 | Please{' '} 28 | upload your receipts. 29 |
30 |
31 | ); 32 | } else { 33 | return ( 34 |
35 | Unfortunately, we’re unable to offer you any travel reimbursement to 36 | McHacks. 37 | 38 |
39 | ); 40 | } 41 | }; 42 | 43 | export default TravelStatusOffered; 44 | -------------------------------------------------------------------------------- /src/shared/Styles/theme.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from 'styled-components'; 2 | 3 | const theme: DefaultTheme = { 4 | colors: { 5 | red: '#F2463A', 6 | redMed: '#F56F65', 7 | redLight: '#F89790', 8 | 9 | blue: '#0069FF', 10 | blueLight: '#0069FF30', 11 | 12 | yellow: '#FFD081', 13 | yellowLight: '#FFEFB6', 14 | 15 | teal: '#48DEE2', 16 | tealLight: '#88FCFF', 17 | 18 | purple: '#5C63AB', 19 | purpleLight: '#5C63AB19', 20 | 21 | greyLight: '#10143712', 22 | 23 | white: '#FFFFFF', 24 | black: '#202020', 25 | black80: '#4D4D4D', 26 | black70: '#636363', 27 | black60: '#797979', 28 | black40: '#A6A6A6', 29 | black30: '#BCBCBC', 30 | black20: '#D2D2D2', 31 | black10: '#E9E9E9', 32 | black5: '#F4F4F4', 33 | }, 34 | inputBorderRadius: '20px', 35 | fonts: { 36 | header: 37 | 'Brown, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif', 38 | body: 'Hind Siliguri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif', 39 | }, 40 | screens: { 41 | smUp: '768px', 42 | mdUp: '992px', 43 | lgUp: '1200px', 44 | }, 45 | }; 46 | 47 | export default theme; 48 | -------------------------------------------------------------------------------- /src/shared/Elements/BackgroundImage.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface IBackgroundImageProps { 4 | top?: string | number; 5 | bottom?: string | number; 6 | left?: string | number; 7 | right?: string | number; 8 | minHeight?: string | number; 9 | imgHeight?: string | number; 10 | minWidth?: string | number; 11 | imgWidth?: string | number; 12 | position?: string; 13 | zIndex?: number; 14 | } 15 | 16 | export const BackgroundImage = styled.img` 17 | position: ${(props: IBackgroundImageProps) => props.position || 'absolute'}; 18 | z-index: ${(props: IBackgroundImageProps) => props.zIndex || '1'}; 19 | user-select: none; 20 | top: ${(props: IBackgroundImageProps) => props.top || ''}; 21 | left: ${(props: IBackgroundImageProps) => props.left || ''}; 22 | bottom: ${(props: IBackgroundImageProps) => props.bottom || ''}; 23 | right: ${(props: IBackgroundImageProps) => props.right || ''}; 24 | min-height: ${(props: IBackgroundImageProps) => props.minHeight || ''}; 25 | min-width: ${(props: IBackgroundImageProps) => props.minWidth || ''}; 26 | height: ${(props: IBackgroundImageProps) => props.imgHeight || 'auto'}; 27 | width: ${(props) => props.imgWidth || 'auto'}; 28 | src: ${(props) => props.src}; 29 | `; 30 | 31 | export default BackgroundImage; 32 | -------------------------------------------------------------------------------- /src/config/statsResponse.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AttendenceOptions, 3 | DietaryRestriction, 4 | HackerStatus, 5 | HackerReviewerStatus, 6 | JobInterest, 7 | ShirtSize, 8 | } from '.'; 9 | 10 | export interface IStatsResponse { 11 | stats: { 12 | total: number; 13 | status: { [key in HackerStatus]: number }; 14 | reviewerStatus: { [key in HackerReviewerStatus]: number }; 15 | reviewerStatus2: { [key in HackerReviewerStatus]: number }; 16 | reviewerName: { [key: string]: number }; 17 | reviewerName2: { [key: string]: number }; 18 | reviewerComments: { [key: string]: number }; 19 | reviewerComments2: { [key: string]: number }; 20 | school: { [key: string]: number }; 21 | degree: { [key: string]: number }; 22 | gender: { [key: string]: number }; 23 | travel: { true: number; false: number }; 24 | ethnicity: { [key: string]: number }; 25 | country: { [key: string]: number }; 26 | jobInterest: { [key in JobInterest]: number }; 27 | major: { [key: string]: number }; 28 | graduationYear: { [key: string]: number }; 29 | dietaryRestriction: { [key in DietaryRestriction & string]: number }; 30 | ShirtSize: { [key in ShirtSize]: number }; 31 | attendancePreference: { [key in AttendenceOptions]: number }; 32 | age: { [key: string]: number }; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /src/shared/HOC/withTokenRedirect.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Navigate } from 'react-router-dom'; 3 | import { FrontendRoute, getTokenFromQuery } from '../../config'; 4 | 5 | enum authStates { 6 | authorized, 7 | unauthorized, 8 | undefined, 9 | } 10 | 11 | const withTokenRedirect =

(Component: React.ComponentType

) => 12 | class extends React.Component { 13 | constructor(props: any) { 14 | super(props); 15 | this.state = { 16 | authState: authStates.undefined, 17 | }; 18 | } 19 | 20 | public async componentDidMount() { 21 | try { 22 | getTokenFromQuery(); 23 | this.setState({ 24 | authState: authStates.authorized, 25 | }); 26 | } catch (e) { 27 | this.setState({ 28 | authState: authStates.unauthorized, 29 | }); 30 | } 31 | } 32 | 33 | public render() { 34 | const { authState } = this.state; 35 | switch (authState) { 36 | case authStates.authorized: 37 | return ; 38 | case authStates.unauthorized: 39 | return ; 40 | default: 41 | return

; 42 | } 43 | } 44 | }; 45 | 46 | export default withTokenRedirect; 47 | -------------------------------------------------------------------------------- /src/shared/Styles/inputStyles.ts: -------------------------------------------------------------------------------- 1 | import { css } from 'styled-components'; 2 | 3 | export interface IInputProps { 4 | isTight?: boolean; 5 | fontWeight?: string; 6 | } 7 | 8 | export const inputStyles = css` 9 | border-radius: 8px; 10 | font-weight: ${(props) => props.fontWeight || 'normal'}; 11 | border: none; 12 | box-shadow: 2px 4px 16px 0px ${(props) => props.theme.colors.greyLight}; 13 | box-sizing: border-box; 14 | display: block; 15 | font-size: 16px; 16 | margin: auto; 17 | margin-top: 12px; 18 | margin-bottom: ${(props) => (props.isTight ? '24px' : '32px')}; 19 | min-height: 40px; 20 | padding-left: 18px; 21 | width: 100%; 22 | transition: 0.25s border ease-in, 0.25s box-shadow ease-in; 23 | color: ${(props) => props.theme.colors.black80}; 24 | font-family: ${(props) => props.theme.fonts.header}; 25 | 26 | &::placeholder { 27 | color: ${(props) => props.theme.colors.black40}; 28 | } 29 | 30 | &:focus { 31 | outline: none; 32 | box-shadow: 2px 2px 16px 2px ${(props) => props.theme.colors.blueLight}; 33 | } 34 | 35 | &:disabled { 36 | border-color: ${(props) => props.theme.colors.black5} !important; 37 | background-color: ${(props) => props.theme.colors.black5} !important; 38 | cursor: not-allowed; 39 | box-shadow: none; 40 | } 41 | `; 42 | 43 | export default inputStyles; 44 | -------------------------------------------------------------------------------- /src/pages/_error.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Helmet from 'react-helmet'; 3 | import { LinkDuo } from '../shared/Elements'; 4 | 5 | import { FrontendRoute, HACKATHON_NAME } from '../config'; 6 | 7 | import Button, { ButtonVariant } from '../shared/Elements/Button'; 8 | import H1 from '../shared/Elements/H1'; 9 | import Paragraph from '../shared/Elements/Paragraph'; 10 | 11 | /** 12 | * Container that renders 404 not found page. 13 | */ 14 | const NotFoundPage: React.FC = () => ( 15 | <> 16 | 17 | Page not found | {HACKATHON_NAME} 18 | 19 | 20 |
21 |

404: Page not found

22 | 23 | The page you're looking for doesn't exists or has been moved 24 | 25 | 26 | 27 | 34 | 35 |
36 | 37 | 45 | 46 | ); 47 | 48 | export default NotFoundPage; 49 | -------------------------------------------------------------------------------- /src/features/Application/PaginationHeader/NumberPageText.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import done from '../../../assets/images/done.svg'; 3 | import { Image } from '../../../shared/Elements'; 4 | import theme from '../../../shared/Styles/theme'; 5 | 6 | interface INumberPageText { 7 | pageNumber: number; 8 | fill: boolean; 9 | check: boolean; 10 | } 11 | 12 | /** 13 | * Displays either a checkmark or page number depending on props 14 | * Should be children prop of NumberBubble 15 | */ 16 | const NumberPageText: React.FC = (props) => { 17 | if (!props.check) { 18 | // Number only displays if we are not displaying a checkmark 19 | const notSelectedTextStyle = { 20 | position: 'relative' as 'relative', 21 | fontSize: '12px', 22 | color: theme.colors.black40, 23 | textAlign: 'center' as 'center', 24 | fontFamily: theme.fonts.header, 25 | lineHeight: '24px', 26 | }; 27 | 28 | const selectedTextStyle = { 29 | ...notSelectedTextStyle, 30 | color: 'white', 31 | }; 32 | const textStyle = props.fill ? selectedTextStyle : notSelectedTextStyle; // Style changes depending on props.fill 33 | 34 | return {props.pageNumber}; 35 | } else { 36 | // Checkmark svg 37 | return ; 38 | } 39 | }; 40 | 41 | export default NumberPageText; 42 | -------------------------------------------------------------------------------- /src/shared/Form/EmailInput.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Input, Label, LabelText } from './'; 3 | 4 | interface IEmailInputProp { 5 | onEmailChanged: (email: string) => void; 6 | disabled?: boolean; 7 | value?: string; 8 | placeholder?: string; 9 | isTight?: boolean; 10 | label: string; 11 | required?: boolean; 12 | } 13 | 14 | export const EmailInput: React.FC = ( 15 | props: IEmailInputProp 16 | ) => { 17 | const placeholder = props.placeholder ? props.placeholder : ''; 18 | return ( 19 | 30 | ); 31 | }; 32 | /** 33 | * Function factory that generates function to handle changes in user's choice. 34 | * @param props The props passed into the EmailInput component. 35 | * @returns the function that handles changes to the choices provided by the user. 36 | */ 37 | function handleChange( 38 | props: IEmailInputProp 39 | ): (event: React.ChangeEvent) => void { 40 | return (event: React.ChangeEvent) => 41 | props.onEmailChanged(event.target.value); 42 | } 43 | -------------------------------------------------------------------------------- /src/shared/Form/FormikElements/FormattedNumber.tsx: -------------------------------------------------------------------------------- 1 | import { FieldProps } from 'formik'; 2 | import * as React from 'react'; 3 | import { NumberFormatValues } from 'react-number-format'; 4 | import { NumberFormatInput } from '..'; 5 | 6 | interface INumberFormatFormikComponent { 7 | label: string; 8 | format: string; 9 | placeholder?: string; 10 | value?: string; 11 | required?: boolean; 12 | disabled?: boolean; 13 | } 14 | 15 | const NumberFormatFormikComponent: React.FunctionComponent< 16 | INumberFormatFormikComponent & FieldProps 17 | > = (props) => { 18 | const placeholder = props.placeholder ? props.placeholder : ''; 19 | 20 | return ( 21 | 30 | ); 31 | }; 32 | /** 33 | * Function factory that generates function to handle changes in user's choice. 34 | * @param props The props passed into the Textarea component. 35 | * @returns the function that handles changes to the choices provided by the user. 36 | */ 37 | function handleChange({ field, form }: FieldProps) { 38 | return (value: NumberFormatValues) => { 39 | form.setFieldValue(field.name, value.value); 40 | }; 41 | } 42 | 43 | export { NumberFormatFormikComponent as FormattedNumber }; 44 | -------------------------------------------------------------------------------- /src/config/skills.ts: -------------------------------------------------------------------------------- 1 | export enum Skills { 2 | ANDROID = 'Android', 3 | ANGULAR = 'Angular', 4 | AI = 'Artificial Intelligence', 5 | BACKEND = 'Backend', 6 | BASH = 'Bash', 7 | C = 'C', 8 | C_PLUS_PLUS = 'C++', 9 | C_SHARP = 'C#', 10 | CSS = 'CSS', 11 | DATA_SCIENCE = 'Data Science', 12 | DESKTOP_APPS = 'Desktop Apps', 13 | DJANGO = 'Django', 14 | EXCEL = 'Excel', 15 | FIGMA = 'Figma', 16 | FPGA = 'FPGA', 17 | FRONTEND = 'Frontend', 18 | GAMEDEV = 'Game Development', 19 | GATSBY = 'Gatsby', 20 | HTML = 'HTML', 21 | IOS = 'iOS', 22 | JAVA = 'Java', 23 | JAVASCRIPT = 'Javascript', 24 | JIRA = 'Jira', 25 | JS = 'JS', 26 | ML = 'Machine Learning', 27 | MOBILE_APPS = 'Mobile Apps', 28 | MONGODB = 'MongoDB', 29 | NLP = 'Natural Language Processing', 30 | NEURAL_NETWORKS = 'Neural Networks', 31 | NEXTJS = 'Next JS', 32 | NODEJS = 'Node JS', 33 | PHP = 'PHP', 34 | PRODUCT_MANAGEMENT = 'Product Management', 35 | PYTHON = 'Python', 36 | REACT = 'React', 37 | RNN = 'RNN', 38 | ROBOTICS = 'Robotics', 39 | RUBY = 'Ruby', 40 | RUBY_ON_RAILS = 'Ruby on Rails', 41 | SKETCH = 'Sketch', 42 | SWIFT = 'Swift', 43 | TS = 'TS', 44 | TURING = 'Turing', 45 | TYPESCRIPT = 'Typescript', 46 | UI_DESIGN = 'UI Design', 47 | UNITY = 'Unity', 48 | UNREAL_ENGINE = 'Unreal Engine', 49 | UX_DESIGN = 'UX Design', 50 | VUE = 'Vue', 51 | WEB_DEV = 'Web Development', 52 | } 53 | export default Skills; 54 | -------------------------------------------------------------------------------- /src/shared/Styles/GlobalStyles.tsx: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | 3 | export const GlobalStyles = createGlobalStyle` 4 | @font-face { 5 | font-family: 'Brown'; 6 | font-style: normal; 7 | font-weight: regular; 8 | src: url('/fonts/lineto-brown-regular.ttf'); 9 | font-display: swap; 10 | } 11 | 12 | @font-face { 13 | font-family: 'Brown'; 14 | font-style: normal; 15 | font-weight: bold; 16 | src: url("/fonts/lineto-brown-bold.ttf"); 17 | font-display: swap; 18 | } 19 | 20 | body { 21 | font-family: ${(props) => props.theme.fonts.body}; 22 | margin: 0; 23 | padding: 0; 24 | 25 | & > #root { 26 | min-height: 100vh; 27 | } 28 | } 29 | 30 | a { 31 | color: ${(props) => props.theme.colors.purple}; 32 | text-decoration: none; 33 | 34 | &:hover { 35 | color: ${(props) => props.theme.colors.yellow}; 36 | 37 | } 38 | 39 | transition: 0.15s color ease-in-out; 40 | } 41 | 42 | h1, h2, h3, h4, h5, h6 { 43 | font-family: ${(props) => props.theme.fonts.header}; 44 | } 45 | 46 | input, textarea, select { 47 | font-family: inherit; 48 | } 49 | 50 | .toast-notification { 51 | z-index: 100000; 52 | } 53 | 54 | @media screen and (min-width: ${(props) => props.theme.screens.smUp}) { 55 | .bm-burger-button, .bm-menu-wrap { 56 | display: none; 57 | } 58 | } 59 | `; 60 | 61 | export default GlobalStyles; 62 | -------------------------------------------------------------------------------- /src/features/Account/validationSchema.ts: -------------------------------------------------------------------------------- 1 | import { array, number, object, string } from 'yup'; 2 | 3 | const getValidationSchema = (isCreate: boolean) => { 4 | const password = isCreate 5 | ? string().min(6, 'Must be at least 6 characters').required('Required') 6 | : string().when('newPassword', { 7 | is: (pass: string) => pass, 8 | then: (schema) => schema.required('Required to change password'), 9 | otherwise: (schema) => schema, 10 | }); 11 | 12 | return object().shape({ 13 | firstName: string().required('Required'), 14 | lastName: string().required('Required'), 15 | email: string().required('Required').email('Must be a valid email'), 16 | password, 17 | newPassword: string().min(6, 'Must be at least 6 characters'), 18 | pronoun: string(), 19 | gender: string(), 20 | dietaryRestrictions: array().of(string()), 21 | phoneNumber: string().test( 22 | 'validPhone', 23 | 'Must be a valid phone number', 24 | (value) => { 25 | const parsedValue = value?.replace(/\D/g, ''); 26 | return ( 27 | !parsedValue || (parsedValue.length > 10 && parsedValue.length < 14) 28 | ); 29 | } 30 | ), 31 | age: number() 32 | .required('Required') 33 | .min(0, 'Age must be a positive number') // Minimum age 34 | .max(100, 'Age must be less than or equal to 100'), // Maximum age 35 | }); 36 | }; 37 | 38 | export default getValidationSchema; 39 | -------------------------------------------------------------------------------- /src/features/Checkin/Reader.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useZxing } from 'react-zxing'; 3 | import styled from 'styled-components'; 4 | 5 | interface IReaderProps { 6 | onScan: (data: string | null) => void; 7 | onError: (err: any) => void; 8 | } 9 | 10 | const Viewport = styled.div` 11 | top: 0; 12 | left: 0; 13 | z-index: 1; 14 | box-sizing: border-box; 15 | border: 50px solid rgba(0, 0, 0, 0.3); 16 | box-shadow: inset 0 0 0 5px rgba(255, 0, 0, 0.5); 17 | position: absolute; 18 | width: 100%; 19 | height: 100%; 20 | `; 21 | 22 | const Video = styled.video` 23 | top: 0; 24 | left: 0; 25 | display: block; 26 | position: absolute; 27 | overflow: hidden; 28 | width: 100%; 29 | height: 100%; 30 | object-fit: cover; 31 | `; 32 | 33 | export const Reader: React.FunctionComponent = ( 34 | props: IReaderProps 35 | ) => { 36 | const { ref } = useZxing({ 37 | onDecodeResult: (result) => props.onScan(result.getText()), 38 | onError: props.onError, 39 | onDecodeError: props.onError, 40 | timeBetweenDecodingAttempts: 500, 41 | }); 42 | 43 | return ( 44 |
45 |
53 | 54 |
56 |
57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /src/shared/Elements/ConfirmModal.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Flex } from '@rebass/grid'; 2 | import React from 'react'; 3 | import { Button, ButtonVariant, StyledModalSmall } from '.'; 4 | 5 | interface IConfirmModalProps { 6 | isOpen: boolean; 7 | onConfirmed: () => void; 8 | onCanceled: () => void; 9 | children?: React.ReactNode; 10 | } 11 | 12 | export const ConfirmModal: React.FC = ( 13 | props: IConfirmModalProps 14 | ) => { 15 | return ( 16 | 23 | 24 | {props.children} 25 | 26 | 27 | 34 | 35 | 36 | 43 | 44 | 45 | 46 | 47 | ); 48 | }; 49 | 50 | export default ConfirmModal; 51 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Before contributing to the McHacks website, please review our [Code of Conduct](https://github.com/hackmcgill/mchacks7/blob/develop/docs/CODE_OF_CONDUCT.md). 4 | 5 | ## Branches 6 | 7 | - Create a new branch from `develop` 8 | - Name branches like `topic/ticket#-short-description`, i.e. `feature/9-styling` 9 | - Some topics: `feature`, `bug`, `refactor` 10 | 11 | ## Commits 12 | 13 | - Use imperative form when writing commit messages, i.e. "Fix margins in..." 14 | - Use sentence case (capitalize the first letter) 15 | - Try to communicate what the change does without having to look at the source code 16 | - List your main change in the unreleased section of the Changelog 17 | 18 | ## Pull Requests 19 | 20 | - Name the PR with a summary of proposed changes 21 | - Complete the entire PR template 22 | - Satisfy the PR checklist before asking for review 23 | - Set `develop` as the base branch unless it's a release (then set base to `master`) 24 | - Squash commits to merge 25 | 26 | ## Releases 27 | 28 | - Create a new branch from `develop` to merge into `master` 29 | - Name the branch like `release/version-number`, i.e. `release/1.4.0` 30 | - For version numbers, we follow [semantic versioning](https://semver.org/) with MAJOR.MINOR.PATCH. 31 | - Create a pull request to merge the release branch into `master` 32 | - Satisfy the entire PR template with a good description for reference 33 | - Add a release tag in the [releases tab](https://github.com/hackmcgill/mchacks7/releases). 34 | -------------------------------------------------------------------------------- /src/features/Application/PaginationHeader/NumberBubble.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import theme from '../../../shared/Styles/theme'; 3 | 4 | interface INumberBubble { 5 | fill: boolean; 6 | current: boolean; 7 | children?: React.ReactNode; 8 | } 9 | 10 | /** 11 | * Page number Bubble that displays child component 12 | */ 13 | const NumberBubble: React.FC = (props) => { 14 | const notSelectedBubble = { 15 | width: '24px', 16 | height: '24px', 17 | borderRadius: '50%', 18 | background: 'none', 19 | border: `2px solid ${theme.colors.black40}`, 20 | boxSizing: 'border-box' as 'border-box', 21 | display: 'flex', 22 | alignItems: 'center', 23 | justifyContent: 'center', 24 | verticalAlign: 'center', 25 | }; 26 | const selectedBubble = { 27 | ...notSelectedBubble, 28 | background: theme.colors.purple, 29 | width: '24px', 30 | height: '24px', 31 | borderRadius: '50%', 32 | border: 'none', 33 | }; 34 | const currentBubble = { 35 | ...selectedBubble, 36 | boxShadow: `2px 2px 16px ${theme.colors.purpleLight}`, 37 | }; 38 | 39 | const bubbleStyle = props.fill 40 | ? props.current 41 | ? currentBubble // Display purple bubble with shadow if page is the current one the user is on 42 | : selectedBubble // Display purple bubble if the bubble should be filled but is not current 43 | : notSelectedBubble; // Display grey bubble if no fill 44 | 45 | return
{props.children}
; 46 | }; 47 | 48 | export default NumberBubble; 49 | -------------------------------------------------------------------------------- /src/features/Team/MemberList/MemberItem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { HackerStatus, IMemberName } from '../../../config'; 4 | import theme from '../../../shared/Styles/theme'; 5 | 6 | const MemberItem: React.FC = (props) => { 7 | const applied = props.status === HackerStatus.HACKER_STATUS_NONE; 8 | 9 | return ( 10 |
11 |
12 |
13 | {props.firstName} {props.lastName} 14 |
15 |
{props.school}
16 |
17 |
18 | {applied ? 'Incomplete Application' : 'Applied'} 19 |
20 | 21 | 49 |
50 | ); 51 | }; 52 | 53 | export default MemberItem; 54 | -------------------------------------------------------------------------------- /src/assets/images/dashboard-confirm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/shared/Form/StyledCreatableSelect.tsx: -------------------------------------------------------------------------------- 1 | import CreatableSelect from 'react-select/creatable'; 2 | import styled from 'styled-components'; 3 | import { inputStyles } from '../Styles/inputStyles'; 4 | 5 | export const StyledCreatableSelect = styled(CreatableSelect)` 6 | font-family: ${(props) => props.theme.fonts.body}; 7 | 8 | .react-select__control { 9 | ${inputStyles} 10 | display: flex; 11 | cursor: text; 12 | &--is-focused { 13 | box-shadow: 2px 2px 16px 0px 14 | ${(props: any) => props.theme.colors.blueLight}; 15 | } 16 | } 17 | 18 | .react-select__indicators { 19 | cursor: pointer; 20 | } 21 | 22 | .react-select__option { 23 | font-weight: normal; 24 | color: ${(props) => props.theme.colors.black70}; 25 | padding-left: 18px; 26 | cursor: pointer; 27 | &--is-selected { 28 | background-color: ${(props) => props.theme.colors.white}; 29 | color: ${(props) => props.theme.colors.black70}; 30 | } 31 | &--is-focused, 32 | &:hover { 33 | background-color: ${(props) => props.theme.colors.black10}; 34 | color: ${(props) => props.theme.colors.black70}; 35 | } 36 | } 37 | 38 | .react-select__value-container { 39 | padding-left: 0; 40 | } 41 | 42 | .react-select__multi-value__label { 43 | background-color: ${(props) => props.theme.colors.black10}; 44 | color: ${(props) => props.theme.colors.purple}; 45 | } 46 | 47 | .react-select__multi-value__remove { 48 | background-color: ${(props) => props.theme.colors.black10}; 49 | } 50 | `; 51 | 52 | export default StyledCreatableSelect; 53 | -------------------------------------------------------------------------------- /src/config/APIRoute.ts: -------------------------------------------------------------------------------- 1 | export enum APIRoute { 2 | // Auth routes 3 | AUTH_LOGIN = 'auth/login', 4 | AUTH_LOGOUT = 'auth/logout', 5 | AUTH_FORGOT_PASS = 'auth/password/forgot', 6 | AUTH_RESET_PASS = 'auth/password/reset', 7 | AUTH_CONFIRM_ACCT = 'auth/confirm', 8 | AUTH_RESEND_CONF_EMAIL = 'auth/confirm/resend', 9 | AUTH_CHANGE_PASS = 'auth/password/change', 10 | 11 | // Account routes 12 | ACCOUNT = 'account', 13 | ACCOUNT_SELF = 'account/self', 14 | ACCOUNT_INVITE = 'account/invite', 15 | // Hacker routes 16 | HACKER = 'hacker', 17 | HACKER_EMAIL = 'hacker/email', 18 | HACKER_CHECKIN = 'hacker/checkin', 19 | HACKER_CONFIRMATION = 'hacker/confirmation', 20 | HACKER_RESUME = 'hacker/resume', 21 | HACKER_SELF = 'hacker/self', 22 | HACKER_STATS = 'hacker/stats', 23 | HACKER_STATUS = 'hacker/status', 24 | HACKER_REVIEWER_STATUS = 'hacker/reviewerStatus', 25 | HACKER_REVIEWER_STATUS2 = 'hacker/reviewerStatus2', 26 | HACKER_REVIEWER_NAME = 'hacker/reviewerName', 27 | HACKER_REVIEWER_NAME2 = 'hacker/reviewerName2', 28 | HACKER_REVIEWER_COMMENTS = 'hacker/reviewerComments', 29 | HACKER_REVIEWER_COMMENTS2 = 'hacker/reviewerComments2', 30 | // Travel routes 31 | TRAVEL = 'travel', 32 | TRAVEL_EMAIL = 'travel/email', 33 | TRAVEL_SELF = 'travel/self', 34 | // Search routes 35 | SEARCH = 'search', 36 | // Settings routes 37 | SETTINGS = 'settings', 38 | // Sponsor routes 39 | SPONSOR = 'sponsor', 40 | SPONSOR_SELF = 'sponsor/self', 41 | 42 | TEAM = 'team', 43 | TEAM_JOIN = 'team/join', 44 | TEAM_LEAVE = 'team/leave', 45 | } 46 | export default APIRoute; 47 | -------------------------------------------------------------------------------- /src/api/team.ts: -------------------------------------------------------------------------------- 1 | import { AxiosPromise } from 'axios'; 2 | import { APIRoute, CACHE_HACKER_KEY, ITeam } from '../config'; 3 | import { ITeamResponse } from '../config/teamGETResponse'; 4 | import LocalCache from '../util/LocalCache'; 5 | import API from './api'; 6 | import APIResponse from './APIResponse'; 7 | 8 | class TeamAPI { 9 | constructor() { 10 | API.createEntity(APIRoute.TEAM); 11 | API.createEntity(APIRoute.TEAM_JOIN); 12 | API.createEntity(APIRoute.TEAM_LEAVE); 13 | } 14 | /** 15 | * create a team. 16 | * @param team The team you want to create. 17 | */ 18 | public create(team: ITeam): AxiosPromise> { 19 | LocalCache.remove(CACHE_HACKER_KEY); 20 | return API.getEndpoint(APIRoute.TEAM).create(team); 21 | } 22 | /** 23 | * Join an existing team 24 | * @param name the team name 25 | */ 26 | public join(name: string): AxiosPromise> { 27 | LocalCache.remove(CACHE_HACKER_KEY); 28 | return API.getEndpoint(APIRoute.TEAM_JOIN).patch({ id: '' }, { name }); 29 | } 30 | /** 31 | * Get information about a team 32 | * @param id the ID of the hacker in a team 33 | */ 34 | public get(id: string): AxiosPromise> { 35 | return API.getEndpoint(APIRoute.TEAM).getOne({ id }); 36 | } 37 | /** 38 | * Current hacker leaves their team 39 | */ 40 | public leave(): AxiosPromise> { 41 | LocalCache.remove(CACHE_HACKER_KEY); 42 | return API.getEndpoint(APIRoute.TEAM_LEAVE).patch({ id: '' }, {}); 43 | } 44 | } 45 | 46 | export const Team = new TeamAPI(); 47 | export default Team; 48 | -------------------------------------------------------------------------------- /src/assets/images/sidebar-home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/features/Nav/Burger.tsx: -------------------------------------------------------------------------------- 1 | import theme from '../../shared/Styles/theme'; 2 | 3 | export const Burger = { 4 | bmBurgerButton: { 5 | position: 'fixed', 6 | width: '30px', 7 | height: '25px', 8 | top: '22px', 9 | right: '30px', 10 | }, 11 | bmBurgerBars: { 12 | background: theme.colors.black40, 13 | borderRadius: '30px', 14 | height: '3.5px', 15 | }, 16 | bmBurgerBarsHover: { 17 | background: theme.colors.red, 18 | }, 19 | bmCrossButton: { 20 | height: '30px', 21 | width: '30px', 22 | top: '22px', 23 | right: '30px', 24 | outline: 'none', 25 | }, 26 | bmCrossButtonHover: { 27 | background: theme.colors.red, 28 | }, 29 | bmCross: { 30 | background: theme.colors.black40, 31 | height: '5px', 32 | width: '30px', 33 | top: '5px', 34 | left: '-15px', 35 | borderRadius: '5px', 36 | }, 37 | bmMenuWrap: { 38 | position: 'fixed', 39 | height: '100%', 40 | }, 41 | bmMenu: { 42 | background: theme.colors.black5, 43 | padding: '2.5em 1.5em 0', 44 | }, 45 | bmMorphShape: { 46 | fill: '#373a47', 47 | }, 48 | bmItemList: { 49 | color: theme.colors.red, 50 | padding: '2rem', 51 | top: '8em', 52 | buttom: '8em', 53 | display: 'grid', 54 | height: '250px', 55 | textAlign: 'center', 56 | fontSize: '24px', 57 | marginTop: '60px', 58 | }, 59 | bmItem: { 60 | textAlign: 'center', 61 | lineHeight: '7rem', 62 | fontSize: '36px', 63 | padding: '40px', 64 | }, 65 | bmOverlay: { 66 | background: 'rgba(0, 0, 0, 0.3)', 67 | }, 68 | }; 69 | 70 | export default Burger; 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [McHacks Dashboard](https://app.mchacks.ca) 2 | 3 | This repository contains the code for the hackathon dashboard of [McHacks](https://mchacks.ca), Canada's favourite hackathon hosted annually at McGill University. It connects with our [API](https://github.com/hackmcgill/hackerAPI) and is hosted at [app.mchacks.ca](https://app.mchacks.ca). 4 | 5 | ## Folder Structure 6 | 7 | ``` 8 | . 9 | ├── .github 10 | ├── .netlify 11 | ├── .vscode 12 | ├── docs 13 | ├── public 14 | | ├── favicon 15 | | ├── fonts 16 | ├── src 17 | | ├── api 18 | | ├── assets 19 | | ├── config 20 | | ├── features 21 | | ├── shared 22 | | └── util 23 | ``` 24 | 25 | 26 | 27 | ## Screenshots 28 | 29 | ### Create / edit your account 30 | 31 | ![Imgur](https://imgur.com/SquZyex.png) 32 | 33 | ### Create / edit your application 34 | 35 | ![Imgur](https://imgur.com/gsyu6Xu.jpg) 36 | 37 | ### Hacker Dashboard 38 | 39 | ![Imgur](https://i.imgur.com/SuCcHuU.png) 40 | 41 | ### Team Viewing 42 | 43 | ![Imgur](https://imgur.com/Zzbnd3o.png) 44 | 45 | ### HackPass QR code for check-in 46 | 47 | ![Imgur](https://imgur.com/pCFlgJc.png) 48 | 49 | ### Staff Dashboard 50 | 51 | ![Imgur](https://imgur.com/MoZykrc.png) 52 | 53 | ### Search with filters, fuzzy search 54 | 55 | ![Imgur](https://imgur.com/GGoUXQm.png) 56 | 57 | ### Single hacker view modal 58 | 59 | ![Imgur](https://imgur.com/DrqP79P.png) 60 | 61 | ### Single hacker view page 62 | 63 | ![Imgur](https://imgur.com/ZyAebHZ.png) 64 | 65 | ### Check-in via QR code or email 66 | 67 | ![Imgur](https://imgur.com/6NSChzs.png) 68 | -------------------------------------------------------------------------------- /src/assets/images/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/pages/Account/Edit.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Helmet from 'react-helmet'; 3 | import ManageAccountContainer, { 4 | ManageAccountModes, 5 | } from '../../features/Account/ManageAccountForm'; 6 | 7 | import * as CONSTANTS from '../../config/constants'; 8 | import { H1, MaxWidthBox } from '../../shared/Elements'; 9 | 10 | import Gears from '../../assets/images/hacker2-dots.svg'; 11 | 12 | const EditAccountPage: React.FC = () => ( 13 | <> 14 | 15 | Edit Profile | {CONSTANTS.HACKATHON_NAME} 16 | 17 | 18 | 24 |
25 | Background 26 |
27 | 28 |

37 | Your Account 38 |

39 | 40 |
41 | 42 | 58 | 59 | ); 60 | 61 | export default EditAccountPage; 62 | -------------------------------------------------------------------------------- /src/shared/Form/LabelText.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Flex } from '@rebass/grid'; 2 | import React from 'react'; 3 | import { OPTIONAL_INPUT, REQUIRED_INPUT } from '../../config'; 4 | import theme from '../Styles/theme'; 5 | import { RequiredInputLabel, SecondaryInfoText } from './'; 6 | 7 | interface ILabelTextProps { 8 | // Label text 9 | label: any; 10 | 11 | // Is this a required field 12 | required?: boolean; 13 | 14 | // Should this field display a * to let user know it's required 15 | // (a field can be required, but still have showRequiredLabel set to false) 16 | showRequiredLabel?: boolean; 17 | 18 | // Should this field display a (optional) message to let user know 19 | // it's safe to skip this field? 20 | showOptionalLabel?: boolean; 21 | 22 | // Subtext underlabel, explaining in more detail 23 | secondaryInfo?: any; 24 | } 25 | 26 | export const LabelText: React.FC = ( 27 | props: ILabelTextProps 28 | ) => { 29 | const requiredText = props.showRequiredLabel ? ( 30 | {REQUIRED_INPUT} 31 | ) : props.showOptionalLabel ? ( 32 | 39 | {OPTIONAL_INPUT} 40 | 41 | ) : null; 42 | 43 | const secondaryInfo = ( 44 | {props.secondaryInfo} 45 | ); 46 | return ( 47 | 48 | 49 | {props.label} 50 | {requiredText} 51 | 52 | {props.secondaryInfo && {secondaryInfo}} 53 | 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/features/Search/HackerSelect.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Sponsor } from '../../api'; 3 | import { Checkbox } from '../../shared/Form'; 4 | import NomineeContext from './Context'; 5 | 6 | interface IProps { 7 | hackerId: string; 8 | } 9 | 10 | interface IState { 11 | isChanging: boolean; 12 | } 13 | 14 | class HackerSelect extends React.Component { 15 | public static contextType = NomineeContext; 16 | public context: React.ContextType; 17 | 18 | constructor(props: IProps) { 19 | super(props); 20 | this.state = { 21 | isChanging: false, 22 | }; 23 | 24 | this.handleChange = this.handleChange.bind(this); 25 | } 26 | public render() { 27 | const { hackerId } = this.props; 28 | if (!this.context || !this.context.nominees) { 29 | return
; 30 | } 31 | const isChecked = this.context.nominees.indexOf(hackerId) > -1; 32 | return ( 33 |
34 | 35 |
36 | ); 37 | } 38 | 39 | private async handleChange(event: React.ChangeEvent) { 40 | const isChecked = event.target.checked; 41 | const { isChanging } = this.state; 42 | const { hackerId } = this.props; 43 | 44 | if (isChanging || !this.context) { 45 | return; 46 | } 47 | 48 | if (isChecked) { 49 | this.context.nominees.push(hackerId); 50 | } else { 51 | this.context.nominees = this.context.nominees.filter( 52 | (n: string) => n !== hackerId 53 | ); 54 | } 55 | 56 | this.setState({ isChanging: true }); 57 | await Sponsor.update(this.context); 58 | this.setState({ isChanging: false }); 59 | } 60 | } 61 | 62 | export default HackerSelect; 63 | -------------------------------------------------------------------------------- /src/shared/Form/PasswordInput.tsx: -------------------------------------------------------------------------------- 1 | import { Flex } from '@rebass/grid'; 2 | import React from 'react'; 3 | import ForgotPasswordLinkComponent from '../../features/Login/ForgotPasswordLink'; 4 | import { Input, Label, LabelText } from './'; 5 | 6 | interface IPasswordInputProp { 7 | onPasswordChanged: (email: string) => void; 8 | label?: string; 9 | required?: boolean; 10 | id?: string; 11 | isTight?: boolean; 12 | value?: string; 13 | placeholder?: string; 14 | hasResetLink?: boolean; 15 | } 16 | 17 | /** 18 | * A password field in the form 19 | * @prop {boolean} hasResetLink should a reset password link be displayed 20 | */ 21 | export const PasswordInput: React.FC = (props) => { 22 | const placeholder = props.placeholder ? props.placeholder : ''; 23 | return ( 24 | 39 | ); 40 | }; 41 | /** 42 | * Function factory that generates function to handle changes in user's choice. 43 | * @param props The props passed into the PasswordInput component. 44 | * @returns the function that handles changes to the choices provided by the user. 45 | */ 46 | function handleChange( 47 | props: IPasswordInputProp 48 | ): (event: React.ChangeEvent) => void { 49 | return (event: React.ChangeEvent) => 50 | props.onPasswordChanged(event.target.value); 51 | } 52 | -------------------------------------------------------------------------------- /src/shared/Form/FormikElements/LongTextInput.tsx: -------------------------------------------------------------------------------- 1 | import { FieldProps } from 'formik'; 2 | import React from 'react'; 3 | import { Label, LabelText } from '../'; 4 | import { Textarea } from '../../Elements'; 5 | 6 | export interface ITextAreaProp { 7 | label: string; 8 | placeholder?: string; 9 | value?: string; 10 | required?: boolean; 11 | maxLength?: number; 12 | style?: object; 13 | showOptionalLabel?: boolean; 14 | } 15 | 16 | export const LongTextInput: React.FC = (props) => { 17 | const placeholder = props.placeholder ? props.placeholder : ''; 18 | const charLeft = 19 | props.maxLength && props.value 20 | ? `${props.value.length}/${props.maxLength} characters` 21 | : ''; 22 | return ( 23 |