From 1b3fad02003a6be4dc90993c8cfef92648be6838 Mon Sep 17 00:00:00 2001 From: Dennis Wang <88702296+denniswangcodes@users.noreply.github.com> Date: Wed, 15 Nov 2023 22:48:39 -0800 Subject: [PATCH] Feature 288 org sign up screens (#348) * added IRS classification dropdown to new org signup UI * updated steps to Figma design 90% similarity * minor text rearrangement * repurposed 306-sign-in-modal modal logic for signup.tsx * added back sample.env file * Put back old code in signupuserandnonprofit.tsk to resolve conflict * Modal working, test modal opens closes successfully * Modal handleClickOutisde still not working, otherwise removed paths to login/signup page and now all pages show modal correctly * finalized css of the modal, added handleClose function on URL change or when the X on the top right is used * resolved comments, removed google and facebook buttons, changed colors to theme.palette * fixed merge conflicts and typescritp conflicts * added modal for join now in header * fixed bug login.tsk path undefined * Linked modal logic to home page's Join Now CTA Button * Fixed styling of pop up modal buttons and divider * refactored steps UI to match figma text fields * decluttered imports * added org online contact information page * decluttered imports and updated text in signupmodal * added already have account and display signup profile logic * improved inut label appearance & sign in click * changed signinmodal.tsx signup button * added disable next until fields completed logic * added chip select color change logic * refined next button logic * removed sign up button on final page * added ts ignore 405 * test * refactored view and component file structure * updated focusAreas tsx to FocusArea tsx * refactoring to parent child structure * finished refactoring parent child component structure * removed unused things * removed linter for unused things * completed refactoring of all five steps children components * minor cleanup after refactor --------- Co-authored-by: etokruto --- .husky/pre-push | 4 - client/src/components/BannerSection.tsx | 15 +- client/src/components/Buttons/CTAButton.tsx | 45 + client/src/components/Header.tsx | 7 +- .../Icons/SvgSignUpContactInfoStep.js | 428 +++++ .../components/Icons/SvgSignUpFinishedStep.js | 1475 ++++++++++++++++ .../Icons/SvgSignUpFocusAreasStep.js | 1321 ++++++++++++++ .../components/Icons/SvgSignUpLocationStep.js | 891 ++++++++++ .../components/Icons/SvgSignUpProfileStep.js | 1513 +++++++++++++++++ client/src/components/Modals/SignInModal.tsx | 22 +- client/src/components/Modals/SignUpModal.tsx | 97 +- .../SignUpUserAndNonprofit.tsx | 1007 ++++------- .../Auth/SignUpUserAndNonprofit/StepFive.tsx | 37 + .../Auth/SignUpUserAndNonprofit/StepFour.tsx | 69 + .../Auth/SignUpUserAndNonprofit/StepOne.tsx | 131 ++ .../Auth/SignUpUserAndNonprofit/StepThree.tsx | 40 + .../Auth/SignUpUserAndNonprofit/StepTwo.tsx | 101 ++ .../Auth/SignUpUserAndNonprofit/StepZero.tsx | 196 +++ client/src/views/FocusAreas.ts | 19 + client/src/views/Signup.tsx | 87 +- client/src/views/SignupNonProfit.tsx | 6 +- client/tsconfig.json | 14 +- 22 files changed, 6774 insertions(+), 751 deletions(-) delete mode 100755 .husky/pre-push create mode 100644 client/src/components/Buttons/CTAButton.tsx create mode 100644 client/src/components/Icons/SvgSignUpContactInfoStep.js create mode 100644 client/src/components/Icons/SvgSignUpFinishedStep.js create mode 100644 client/src/components/Icons/SvgSignUpFocusAreasStep.js create mode 100644 client/src/components/Icons/SvgSignUpLocationStep.js create mode 100644 client/src/components/Icons/SvgSignUpProfileStep.js create mode 100644 client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFive.tsx create mode 100644 client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFour.tsx create mode 100644 client/src/components/Users/Auth/SignUpUserAndNonprofit/StepOne.tsx create mode 100644 client/src/components/Users/Auth/SignUpUserAndNonprofit/StepThree.tsx create mode 100644 client/src/components/Users/Auth/SignUpUserAndNonprofit/StepTwo.tsx create mode 100644 client/src/components/Users/Auth/SignUpUserAndNonprofit/StepZero.tsx create mode 100644 client/src/views/FocusAreas.ts diff --git a/.husky/pre-push b/.husky/pre-push deleted file mode 100755 index 5d049a87..00000000 --- a/.husky/pre-push +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -cd server && npm run checks && cd ../client && npm run checks && cd .. diff --git a/client/src/components/BannerSection.tsx b/client/src/components/BannerSection.tsx index b4225c1c..2e14319d 100644 --- a/client/src/components/BannerSection.tsx +++ b/client/src/components/BannerSection.tsx @@ -1,9 +1,10 @@ +import React, { useContext } from 'react'; import { Grid, Typography, Box } from '@mui/material'; import { makeStyles } from 'tss-react/mui'; import type { Theme } from '@mui/material/styles'; import MainImage from '../assets/banner-section-main.svg'; import { CTAHeroButton } from './Buttons/Button'; -import { useHistory } from 'react-router-dom'; +import { ModalContext } from './../providers/ModalProvider'; const useStyles = makeStyles()((theme: Theme) => ({ gridTitle: { @@ -44,15 +45,23 @@ function BannerText() { function BannerSection() { const { classes } = useStyles(); - const history = useHistory(); + const modalContext = useContext(ModalContext); + const { openModal } = modalContext; + const handleOpenModal = (modalType: 'SignIn' | 'SignUp') => { + if (modalType === 'SignIn') { + openModal('SignIn'); + } else if (modalType === 'SignUp') { + openModal('SignUp'); + } + }; return ( - history.push('/signup-citizen')} /> + handleOpenModal('SignUp')} /> ({ + CTAButton: { + color: 'white', + backgroundColor: '#EF6A60', + borderRadius: '10px', + fontFamily: 'Poppins', + fontWeight: 'semi-bold', + border: 'none', + '&:hover': { + backgroundColor: '#EF6A60', + cursor: 'pointer', + }, + }, +})); + +type Props = { + text: string; +}; + +function CTAButton({ text }: Props) { + const { classes } = useStyles(); + const modalContext = useContext(ModalContext); + const { openModal } = modalContext; + + return ( + + ); +} + +export default CTAButton; diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx index 1a042941..f351f73d 100644 --- a/client/src/components/Header.tsx +++ b/client/src/components/Header.tsx @@ -109,7 +109,7 @@ function Header() { const modalContext = useContext(ModalContext); const { openModal } = modalContext; - const handleoOpenModal = (modalType: 'SignIn' | 'SignUp') => { + const handleOpenModal = (modalType: 'SignIn' | 'SignUp') => { if (modalType === 'SignIn') { openModal('SignIn'); } else if (modalType === 'SignUp') { @@ -489,12 +489,11 @@ function Header() { ) : ( <> - {/* TODO: Use () => handleoOpenModal('SignUp') when implemented */} history.push('/signup-citizen')} + onClick={() => handleOpenModal('SignUp')} > - diff --git a/client/src/components/Icons/SvgSignUpContactInfoStep.js b/client/src/components/Icons/SvgSignUpContactInfoStep.js new file mode 100644 index 00000000..e1dcc241 --- /dev/null +++ b/client/src/components/Icons/SvgSignUpContactInfoStep.js @@ -0,0 +1,428 @@ +import * as React from 'react'; +const SvgSignUpContactInfoStep = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default SvgSignUpContactInfoStep; diff --git a/client/src/components/Icons/SvgSignUpFinishedStep.js b/client/src/components/Icons/SvgSignUpFinishedStep.js new file mode 100644 index 00000000..b4237964 --- /dev/null +++ b/client/src/components/Icons/SvgSignUpFinishedStep.js @@ -0,0 +1,1475 @@ +import * as React from 'react'; +const SvgSignUpFinishedStep = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default SvgSignUpFinishedStep; diff --git a/client/src/components/Icons/SvgSignUpFocusAreasStep.js b/client/src/components/Icons/SvgSignUpFocusAreasStep.js new file mode 100644 index 00000000..43c0f32c --- /dev/null +++ b/client/src/components/Icons/SvgSignUpFocusAreasStep.js @@ -0,0 +1,1321 @@ +import * as React from 'react'; +const SvgSignUpInterestsStep = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default SvgSignUpInterestsStep; diff --git a/client/src/components/Icons/SvgSignUpLocationStep.js b/client/src/components/Icons/SvgSignUpLocationStep.js new file mode 100644 index 00000000..b3d49eff --- /dev/null +++ b/client/src/components/Icons/SvgSignUpLocationStep.js @@ -0,0 +1,891 @@ +import * as React from 'react'; +const SvgSignUpLocationStep = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default SvgSignUpLocationStep; diff --git a/client/src/components/Icons/SvgSignUpProfileStep.js b/client/src/components/Icons/SvgSignUpProfileStep.js new file mode 100644 index 00000000..f1a82f19 --- /dev/null +++ b/client/src/components/Icons/SvgSignUpProfileStep.js @@ -0,0 +1,1513 @@ +import * as React from 'react'; +const SvgSignUpProfileStep = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default SvgSignUpProfileStep; diff --git a/client/src/components/Modals/SignInModal.tsx b/client/src/components/Modals/SignInModal.tsx index 2607f5b9..d123054e 100644 --- a/client/src/components/Modals/SignInModal.tsx +++ b/client/src/components/Modals/SignInModal.tsx @@ -6,7 +6,8 @@ import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import Dialog from '@mui/material/Dialog'; import DialogContent from '@mui/material/DialogContent'; - +import { useContext } from 'react'; +import { ModalContext } from '../../providers/ModalProvider'; import CloseIcon from '@mui/icons-material/Close'; import IconButton from '@mui/material/IconButton'; @@ -46,13 +47,15 @@ interface Error { type: '' | 'email' | 'password'; message: string; } + const SignInModal = React.forwardRef( ({ closeModal, className }, ref) => { const history = useHistory(); const [isLoading, setIsLoading] = React.useState(false); const [error, setError] = React.useState(null); const { setUser } = React.useContext(UserContext); - + const modalContext = useContext(ModalContext); + const { openModal } = modalContext; const [formData, setFormData] = React.useState(initialFormData); const handleCloseModal = React.useCallback(() => { @@ -174,7 +177,20 @@ const SignInModal = React.forwardRef( Not signed up yet?{' '} - Sign Up + openModal('SignUp')} + > + Sign Up + diff --git a/client/src/components/Modals/SignUpModal.tsx b/client/src/components/Modals/SignUpModal.tsx index 822d2195..b2a67434 100644 --- a/client/src/components/Modals/SignUpModal.tsx +++ b/client/src/components/Modals/SignUpModal.tsx @@ -1,5 +1,16 @@ -// import Button from '@mui/material/Button'; import React from 'react'; +import routes from '../../routes/routes'; +import Grid from '@mui/material/Grid'; +import Dialog from '@mui/material/Dialog'; +import Button from '@mui/material/Button'; +import Divider from '@mui/material/Divider'; +import CloseIcon from '@mui/icons-material/Close'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import DialogContent from '@mui/material/DialogContent'; +import { Link } from 'react-router-dom'; +import { makeStyles } from 'tss-react/mui'; +import type { Theme } from '@mui/material/styles'; interface SignUpModalProps { closeModal: () => void; @@ -14,19 +25,85 @@ interface SignUpModalProps { }; } +const useStyles = makeStyles()((theme: Theme) => ({ + root: { + border: 0, + maxWidth: '1000px', + maxHeight: '100px', + minWidth: '100px', + minHeight: '100px', + padding: '0px', + }, +})); + const SignUpModal = React.forwardRef( ({ closeModal, className }, ref) => { - // const handleCloseModal = () => { - // closeModal(); - // }; + const handleCloseModal = () => { + closeModal(); + }; + + const { classes } = useStyles(); return ( - //
- //

Sign Up

- // {/* ... SIGNUP CODE ... */} - // - //
-
+
+ + + + + + + + + Welcome Aboard! + + + + + + + + + + + + + + + + + + + +
+
); }, ); diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/SignUpUserAndNonprofit.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/SignUpUserAndNonprofit.tsx index c4588efe..b76539a3 100644 --- a/client/src/components/Users/Auth/SignUpUserAndNonprofit/SignUpUserAndNonprofit.tsx +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/SignUpUserAndNonprofit.tsx @@ -1,726 +1,375 @@ import * as React from 'react'; -import { - Box, - Button, - Checkbox, - FormControlLabel, - FormHelperText, - FormLabel, - LinearProgress, - MenuItem, - OutlinedInput, - Select, - SelectChangeEvent, - TextField, -} from '@mui/material'; +import StepZero from './StepZero'; +import StepOne from './StepOne'; +import StepTwo from './StepTwo'; +import StepThree from './StepThree'; +import StepFour from './StepFour'; +import StepFive from './StepFive'; +import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; -import Grid from '@mui/material/Grid'; -import CheckIcon from '@mui/icons-material/Check'; +import SvgSignUpContactInfoStep from '../../../Icons/SvgSignUpContactInfoStep'; +import SvgSignUpLocationStep from '../../../Icons/SvgSignUpLocationStep'; +import SvgSignUpfocusAreasStep from '../../../Icons/SvgSignUpFocusAreasStep'; +import SvgSignUpProfileStep from '../../../Icons/SvgSignUpProfileStep'; +import SvgSignUpFinishedStep from '../../../Icons/SvgSignUpFinishedStep'; +import type { Theme } from '@mui/material/styles'; +import { useEffect } from 'react'; +import { focusAreas } from '../../../../views/FocusAreas'; +import { makeStyles } from 'tss-react/mui'; +import { UserContext } from '../../../../providers'; +import { placeholderImg } from '../../../../assets/temp'; +import { APP_API_BASE_URL, US_STATE_NAMES } from '../../../../configs'; +import { Box, MenuItem, SelectChangeEvent, Chip } from '@mui/material'; -import routes from '../../../../routes/routes'; -import StyledLink from '../../../StyledLink'; +interface UserSignupData { + organization_name: string; + organization_phone: string; + street: string; + city: string; + state: string; + zip_code: string; + employer_identification_number: string; + irs_classification: string; + first_name: string; + last_name: string; + role: string; + email: string; + password: string; + accept_terms?: boolean; + email_notification_opt_out?: boolean; + website: string; + instagram: string; + facebook: string; + twitter: string; + focusAreas: string[]; + bio: string; +} -import { useMutation, useQuery } from 'react-query'; -import { AxiosError, AxiosResponse } from 'axios'; -import { green } from '@mui/material/colors'; -import SimpleSnackbar from '../../../SimpleSnackbar'; -import { calculateIsValid, validationSchema } from './validation-rules'; -import { useStyles } from './styles'; -import { FormData } from './FormData'; -import { Controller, useForm } from 'react-hook-form'; -import { yupResolver } from '@hookform/resolvers/yup'; -import { classifications } from './Classifications'; -import { httpGetValidateEin, httpPostNonprofitSignup } from './http-sing-up-nonprofit'; -import InputMask from 'react-input-mask'; -import { ModalContext } from '../../../../providers/ModalProvider'; -import { useContext } from 'react'; - -const defaultOrg: FormData = { - name: '', - doing_business_as: '', +const initialFormData: UserSignupData = { + organization_name: '', + organization_phone: '', + street: '', city: '', state: '', - ein: '', - description: '', - website: '', - address: '', - phone: '', - nonprofit_classification: '', - firstName: '', + zip_code: '', + employer_identification_number: '', + irs_classification: '', + first_name: '', last_name: '', + role: '', email: '', password: '', - confirmPassword: '', accept_terms: false, - image_url: '', email_notification_opt_out: false, + website: '', + instagram: '', + facebook: '', + twitter: '', + focusAreas: [], + bio: '', }; -export const SignUpUserAndNonprofit = () => { +const useStyles = makeStyles()((theme: Theme) => ({ + sideImg: { + backgroundImage: `url("${placeholderImg}")`, + backgroundSize: 'cover', + backgroundPosition: 'center center', + backgroundRepeat: 'no-repeat', + borderTopRightRadius: '15px', + borderBottomRightRadius: '15px', + }, + signUpContainer: { + margin: theme.spacing(5), + }, + button: { + borderRadius: 0, + height: 44, + textTransform: 'none', + backgroundColor: '#C4C4C4', + color: 'white', + }, + header: { + fontWeight: 'bold', + paddingBottom: '40px', + }, + input: { + height: 44, + border: '1px solid #C4C4C4', + borderRadius: 10, + boxSizing: 'border-box', + padding: theme.spacing(1), + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + fontSize: 14, + marginBottom: 20, + }, + label: { + color: '#000000', + fontWeight: 'bold', + textAlign: 'left', + }, + chip: { + boxShadow: '0px 1px 2px rgba(0, 0, 0, 0.25)', + height: 44, + }, +})); + +function SignupNonProfit() { const { classes } = useStyles(); - const { openModal } = useContext(ModalContext); - const [activeStep, setActiveStep] = React.useState(0); - const [einStepIsValid, setEINStepIsValid] = React.useState(false); - const [einApiValidateError, setEinApiValidateError] = React.useState(''); - const [triggerEinSearch, setTriggerEinSearch] = React.useState(false); - const [submitSuccessMessage, setSubmitSuccessMessage] = React.useState(''); - const [submitErrorMessage, setSubmitErrorMessage] = React.useState(''); - const { - control, - handleSubmit, - setValue, - getValues, - reset, - formState: { errors, dirtyFields }, - } = useForm({ - defaultValues: defaultOrg, - mode: 'onChange', - resolver: yupResolver(validationSchema), - }); + const [selectedChips, setSelectedChips] = React.useState([]); + const [disableNext, setDisableNext] = React.useState(true); + const [activeStep, setActiveStep] = React.useState(0); + const [isLoading, setIsLoading] = React.useState(false); + const [emailError, setEmailError] = React.useState(''); + const [formData, setFormData] = React.useState(initialFormData); + const { setUser } = React.useContext(UserContext); - const handleNext = () => { - setActiveStep((prevActiveStep) => prevActiveStep + 1); + const makeChips = () => { + return focusAreas.map((focusArea) => { + return ( + togglefocusArea(focusArea)} + color={selectedChips.includes(focusArea) ? 'primary' : 'default'} + /> + ); + }); }; - const handleBack = () => { - setActiveStep((prevActiveStep) => prevActiveStep - 1); + const togglefocusArea = (focusArea: string): void => { + const existingfocusAreaIdx = formData.focusAreas.findIndex( + (existingfocusArea) => existingfocusArea === focusArea, + ); + if (existingfocusAreaIdx !== -1) { + formData.focusAreas.splice(existingfocusAreaIdx, 1); + setSelectedChips(formData.focusAreas); + } else { + formData.focusAreas.push(focusArea); + setSelectedChips(formData.focusAreas); + } + setFormData({ ...formData, focusAreas: formData.focusAreas }); + console.log(formData.focusAreas); }; - const stepOneInvalid = !dirtyFields.ein || !!errors.ein || !einStepIsValid; - - const stepTwoInvalid = - !!errors.name?.message || - !!errors.description?.message || - !!errors.address?.message || - !!errors.city?.message || - !!errors.state?.message || - !!errors.phone?.message || - !!errors.website?.message || - !getValues().nonprofit_classification; - - const handleEinApiValidationError = (errorResponse: Object) => { - const res = Object.entries(errorResponse); - const { status } = Object.fromEntries(res).response; - switch (status) { - case 404: - setEinApiValidateError('Not Found'); - break; - - default: - setEinApiValidateError('Server Error'); - break; - } + const makeStateSelectOptions = () => { + return US_STATE_NAMES.map((state) => { + return {state}; + }); }; - const onSubmit = () => { - orgSignUpMutation.mutate(getValues()); + const handleSelectChange = (event: SelectChangeEvent, child: React.ReactNode): void => { + const { name, value }: { name: string; value: string } = event.target; + setFormData((fData) => ({ + ...fData, + [name]: value, + })); + nextOrNot(activeStep); }; - const onOrgSignUpSuccess = (): void => { - reset(); - setSubmitSuccessMessage('Organization created successfully. Please log in'); + const handleChange = (evt: React.ChangeEvent): void => { + const { name, value, checked }: { name: string; value: string; checked: boolean } = evt.target; + console.log('CHECK IS HERE', checked); + setFormData((fData) => ({ + ...fData, + [name]: name === 'accept_terms' ? checked : value, + [name]: name === 'email_notification_opt_out' ? checked : value, + })); + nextOrNot(activeStep); }; - const onOrgSignUpError = (err: any): void => { - if (err.response.data.status === 409) { - setSubmitErrorMessage(err.response.data.message ?? 'Unable to save'); + const handleSubmit = async (evt: React.FormEvent) => { + evt.preventDefault(); + setIsLoading(true); + delete formData.accept_terms; + const res = await fetch(`${APP_API_BASE_URL}/auth/register`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData), + }); + const data = await res.json(); + setIsLoading(false); + if (data.status === 409) { + setEmailError(data.message); } else { - setSubmitErrorMessage(`Unable to create account`); + setUser(data); + handleNext(); } }; - const save = (data: FormData) => { - setSubmitErrorMessage(''); - return httpPostNonprofitSignup(data); + const handleNext = () => { + setDisableNext(true); + setActiveStep((prevActiveStep) => prevActiveStep + 1); }; - const orgSignUpMutation = useMutation, AxiosError, FormData, Error>( - (formData: FormData) => save(formData), - { - onSuccess: onOrgSignUpSuccess, - onError: (err) => onOrgSignUpError(err), - }, - ); - - const orgValidateEinQuery = useQuery< - AxiosResponse, - any, - { ein: string; name: string }, - string[] - >({ - enabled: triggerEinSearch, - queryKey: ['orgValidateEinQuery', getValues().ein], - queryFn: ({ queryKey }) => { - const [, ein] = queryKey; - return httpGetValidateEin(ein); - }, - onSuccess: (res: any) => { - setTriggerEinSearch(false); - setEINStepIsValid(true); - setValue('name', res.data.name); - }, - onError: (res: any) => { - setEINStepIsValid(false); - setTriggerEinSearch(false); - setValue('name', ''); - handleEinApiValidationError(res); - }, - retry: 0, - }); - - const orgValidateStateQuery = { - isError: false, - isSuccess: true, - isLoading: false, + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); }; - const orgValidateCityQuery = { - isError: false, - isSuccess: true, - isLoading: false, + const nextOrNot = (step: number) => { + if (step === 0) { + if ( + formData.organization_name.length > 0 && + formData.organization_phone.length > 0 && + formData.street.length > 0 && + formData.city.length > 0 && + formData.state.length > 0 && + formData.employer_identification_number.length > 0 && + formData.irs_classification.length > 0 + ) { + setDisableNext(false); + } + } + if (step === 1) { + if ( + formData.first_name.length > 0 && + formData.last_name.length > 0 && + formData.role.length > 0 && + formData.email.length > 0 && + formData.password.length > 0 && + !formData.accept_terms + ) { + setDisableNext(false); + } + } + if (step === 2 || step === 3 || step === 4) { + setDisableNext(false); + } }; - return ( - - - - - - - Let's get started. - - - - - Already have an account?  - {' '} - - + useEffect(() => { + nextOrNot(activeStep); + }); - - {submitSuccessMessage && } - {submitErrorMessage && } -
+ return ( + + + {(activeStep === 0 && ) || + (activeStep === 1 && ) || + (activeStep === 2 && ) || + (activeStep === 3 && ) || + (activeStep === 4 && ) || + (activeStep === 5 && )} + + + + + {activeStep === 0 && ( - - - ( - - )} - /> - - - ( - <> - - City - - { - if (!errors.ein) { - console.log('add validation here'); - } - }} - error={!!errors.city?.message} - helperText={errors.city?.message ?? ''} - /> - - )} - /> - {orgValidateCityQuery.isLoading ? ( - - ) : ( - <> - {orgValidateCityQuery.isError && ( - {`Invalid EIN ${einApiValidateError}`} - )} - {orgValidateCityQuery.isSuccess && !errors.city && ( - - - - )} - - )} - - - ( - <> - - State - - { - if (!errors.state) { - console.log('add validation here'); - } - }} - error={!!errors.state?.message} - /> - - )} - /> - {orgValidateStateQuery.isLoading ? ( - - ) : ( - <> - {orgValidateStateQuery.isError && ( - {``} - )} - {orgValidateStateQuery.isSuccess && !errors.state && ( - - - - )} - - )} - - - ( - <> - - Employer Identification Number (EIN) - - { - if (!errors.ein) { - setTriggerEinSearch(true); - } - }} - error={!!errors.ein?.message} - helperText={errors.ein?.message ?? ''} - /> - - )} - /> - {orgValidateEinQuery.isLoading ? ( - - ) : ( - <> - {orgValidateEinQuery.isError && ( - {`Invalid EIN ${einApiValidateError}`} - )} - {orgValidateEinQuery.isSuccess && !errors.ein && ( - - - - )} - - )} - - - ( - <> - - IRS Nonprofit Organization Classification - - { - if (!errors.nonprofit_classification) { - console.log('add validation here'); - } - }} - error={!!errors.nonprofit_classification?.message} - helperText={errors.nonprofit_classification?.message ?? ''} - /> - - )} - /> - - + )} - {activeStep === 1 && ( - - - ( - - )} - /> - - - ( - - )} - /> - - - ( - - )} - /> - - - ( - - - - )} - /> - - - ( - - - - )} - /> - - - ( - - )} - /> - - - ( - <> - IRS Nonprofit Organization Classification - - - )} - /> - - {errors.nonprofit_classification - ? errors.nonprofit_classification.message - : ''} - - - + )} - {activeStep === 2 && ( - - - ( - - )} - /> - - - ( - - )} - /> - - - ( - - )} - /> - - - ( - - )} - /> - - - ( - - )} - /> - - - ( - - } - label={ - - } - /> - )} - /> - - - ( - - } - label={} - /> - )} - /> - - + )} - - - - + {activeStep === 3 && ( + + )} + {activeStep === 4 && ( + + )} + {activeStep === 5 && } + + + + + + {(activeStep === 0 || + activeStep === 1 || + activeStep === 2 || + activeStep === 3 || + activeStep === 4) && ( + + )} + {(activeStep === 0 || + activeStep === 1 || + activeStep === 2 || + activeStep === 3 || + activeStep === 4) && ( - - {activeStep === 0 && ( - - )} - {activeStep === 1 && ( - - )} - {activeStep === 2 && ( - - )} - - - - -
-
-
-
+ )} + + +
+ + {isLoading && Loading} + + + + ); -}; +} + +export default SignupNonProfit; diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFive.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFive.tsx new file mode 100644 index 00000000..03d54f14 --- /dev/null +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFive.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import Typography from '@mui/material/Typography'; +import { Box } from '@mui/material'; + +type TStepOneProps = { + formData: { + first_name: string; + last_name: string; + email: string; + }; + classes: Record<'header' | 'input' | 'label', string>; +}; + +export default function StepFive({ classes, formData }: TStepOneProps) { + console.log('FINAL FORM BRO', formData); + return ( + + + Sign up almost complete! + + + {formData.first_name} {formData.last_name} + + {formData.email} + + Please check your e-mail to finish the identity verification process. + Afterwards, start contributing! + + + ); +} diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFour.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFour.tsx new file mode 100644 index 00000000..fa73e1d1 --- /dev/null +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepFour.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import Grid from '@mui/material/Grid'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import { Box, Avatar, TextField } from '@mui/material'; + +type TStepOneProps = { + formData: { + bio: string; + }; + classes: Record<'header' | 'input' | 'label', string>; + handleChange: (evt: React.ChangeEvent) => void; +}; + +export default function StepFour({ classes, formData, handleChange }: TStepOneProps) { + return ( + + + Finalize your organization's profile. + + + A logo, image, or icon that represents your organization. + + + + + + + + + + + + + A summary about your organization at a glance. + + + + + ); +} diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepOne.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepOne.tsx new file mode 100644 index 00000000..d3049b3d --- /dev/null +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepOne.tsx @@ -0,0 +1,131 @@ +import React from 'react'; +import Grid from '@mui/material/Grid'; +import Input from '@mui/material/Input'; +import routes from '../../../../routes/routes'; +import Typography from '@mui/material/Typography'; +import Checkbox from '@mui/material/Checkbox'; +import EmailInput from '../../../Users/Auth/EmailInput'; +import PasswordInput from '../../../Users/Auth/PasswordInput'; +import StyledLink from '../../../StyledLink'; +import FormControl from '@mui/material/FormControl'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { Box } from '@mui/material'; + +type TStepOneProps = { + formData: { + first_name: string; + last_name: string; + role: string; + email: string; + password: string; + accept_terms?: boolean; + }; + emailError: string; + classes: Record<'header' | 'input', string>; + handleChange: (evt: React.ChangeEvent) => void; +}; + +export default function StepOne({ classes, formData, emailError, handleChange }: TStepOneProps) { + return ( + + + Tell us about yourself. + + Representative Information + + + + + + + + + + + + + + + + + + + + + + + + + } + label={ + + } + /> + + + ); +} diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepThree.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepThree.tsx new file mode 100644 index 00000000..d9aad0de --- /dev/null +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepThree.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import { Box } from '@mui/material'; + +type TStepOneProps = { + formData: { + focusAreas: string[]; + }; + classes: Record<'header' | 'input', string>; + makeChips: () => any; +}; + +export default function StepThree({ classes, formData, makeChips }: TStepOneProps) { + return ( + + + Tell us about your organization's focus area(s). + + + + + What type of work is your organization invovled with? + + + + + {makeChips()} + + + + ); +} diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepTwo.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepTwo.tsx new file mode 100644 index 00000000..07476b67 --- /dev/null +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepTwo.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import Grid from '@mui/material/Grid'; +import Input from '@mui/material/Input'; +import Typography from '@mui/material/Typography'; +import FormControl from '@mui/material/FormControl'; +import { Box } from '@mui/material'; + +type TStepOneProps = { + formData: { + website: string; + facebook: string; + instagram: string; + twitter: string; + }; + classes: Record<'header' | 'input', string>; + handleChange: (evt: React.ChangeEvent) => void; +}; + +export default function StepTwo({ classes, formData, handleChange }: TStepOneProps) { + return ( + + + How can others reach you? + + Contact Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepZero.tsx b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepZero.tsx new file mode 100644 index 00000000..9fa5f063 --- /dev/null +++ b/client/src/components/Users/Auth/SignUpUserAndNonprofit/StepZero.tsx @@ -0,0 +1,196 @@ +import React, { useContext } from 'react'; +import Grid from '@mui/material/Grid'; +import Input from '@mui/material/Input'; +import Typography from '@mui/material/Typography'; +import { ModalContext } from '../../../../providers/ModalProvider'; +import { classifications } from '../../../Users/Auth/SignUpUserAndNonprofit/Classifications'; +import { Box, Select, MenuItem, OutlinedInput, SelectChangeEvent } from '@mui/material'; + +type TStepOneProps = { + formData: { + organization_name: string; + organization_phone: string; + street: string; + city: string; + state: string; + zip_code: string; + employer_identification_number: string; + irs_classification: string; + }; + classes: Record<'header' | 'input', string>; + handleNext: () => void; + handleChange: (evt: React.ChangeEvent) => void; + handleSelectChange: (event: SelectChangeEvent, child: React.ReactNode) => void; + makeStateSelectOptions: () => any; +}; + +export default function StepZero({ + classes, + formData, + handleChange, + handleSelectChange, + makeStateSelectOptions, +}: TStepOneProps) { + const modalContext = useContext(ModalContext); + const { openModal } = modalContext; + + return ( + + + Let's get started! + + + + About Your Organization + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Already have an account? + openModal('SignIn')} + > + Sign In + + + + + + + + ); +} diff --git a/client/src/views/FocusAreas.ts b/client/src/views/FocusAreas.ts new file mode 100644 index 00000000..e8d9248f --- /dev/null +++ b/client/src/views/FocusAreas.ts @@ -0,0 +1,19 @@ +export const focusAreas: string[] = [ + 'Animal Care & Services', + 'Poverty', + 'Housing & Homeless', + 'Youth & Children', + 'Disaster Relief', + 'Health Care & Welness', + 'Environment & Sustainability', + 'Sports & Recreation', + 'Seniors', + 'Religion, Faith & Spirituality', + 'Civic Engagement', + 'LGTBQIA+', + 'Civil Rights & Advocacy', + 'Military & Veterans', + 'Social Justice', + 'Education & Literacy', + 'Arts & Culture', +]; diff --git a/client/src/views/Signup.tsx b/client/src/views/Signup.tsx index fb84de60..a21256fc 100644 --- a/client/src/views/Signup.tsx +++ b/client/src/views/Signup.tsx @@ -1,12 +1,19 @@ import * as React from 'react'; import { Link } from 'react-router-dom'; +import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import { makeStyles } from 'tss-react/mui'; -import { Grid, Container, Button } from '@mui/material'; - +import { Grid, Button } from '@mui/material'; import type { Theme } from '@mui/material/styles'; - +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; import routes from '../routes/routes'; +// import { Container } from '@mui/material'; keep for future commits +// import StyledLink from '../components/StyledLink'; +// import TextDivider from '../components/TextDivider'; +// import DialogContentText from '@mui/material/DialogContentText'; const useStyles = makeStyles()((theme: Theme) => ({ imageBackground: { @@ -37,39 +44,49 @@ const useStyles = makeStyles()((theme: Theme) => ({ function Signup() { const { classes } = useStyles(); - return ( - - - Welcome! - - - Select your account type. - - - Already have an account? Log In{' '} - + const [open, setOpen] = React.useState(true); + const onClose = (e: any, reason: string) => { + if (reason !== 'backdropClick') { + setOpen(false); + } + }; - - -
- - Are you a non-profit organization? - - - - -
- -
- - Are you an individual citizen? - - - - -
-
-
+ const buttonClose = () => { + setOpen(false); + }; + + return ( +
+ +
+ + Welcome! + + {/* */} + + Account Type + + + + + + + + + + + + + + {/* */} + + + + + +
+
+
); } diff --git a/client/src/views/SignupNonProfit.tsx b/client/src/views/SignupNonProfit.tsx index 3a6e2cd7..2ad3713e 100644 --- a/client/src/views/SignupNonProfit.tsx +++ b/client/src/views/SignupNonProfit.tsx @@ -1,7 +1,7 @@ -import { SignUpUserAndNonprofit } from '../components/Users/Auth/SignUpUserAndNonprofit/SignUpUserAndNonprofit'; +import SignUpUserAndNonprofit from '../components/Users/Auth/SignUpUserAndNonprofit/SignUpUserAndNonprofit'; -function SignupNonProfit() { +function SignUpUserAndNonprofitView() { return ; } -export default SignupNonProfit; +export default SignUpUserAndNonprofitView; diff --git a/client/tsconfig.json b/client/tsconfig.json index 764fd6d9..99bd16be 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -19,13 +15,11 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - + //Material-UI required below: "noImplicitAny": true, "noImplicitThis": true, - "strictNullChecks": true, + "strictNullChecks": true }, - "include": [ - "src" - ] + "include": ["src"] }