tutorbookapp/tutorbook

View on GitHub
components/request.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import { FormEvent, useCallback, useState } from 'react';
import { TextField } from '@rmwc/textfield';
import axios from 'axios';
import useTranslation from 'next-translate/useTranslation';

import AvailabilitySelect from 'components/availability-select';
import Button from 'components/button';
import Loader from 'components/loader';
import SubjectSelect from 'components/subject-select';
import Title from 'components/title';

import { Subject } from 'lib/model/subject';
import { User } from 'lib/model/user';
import { loginWithGoogle } from 'lib/firebase/login';
import { useOrg } from 'lib/context/org';

export default function RequestForm(): JSX.Element {
  const { org } = useOrg();
  const { t } = useTranslation();
  const [user, setUser] = useState<User>(new User());
  const [subjects, setSubjects] = useState<Subject[]>([]);
  const [description, setDescription] = useState<string>('');
  const [error, setError] = useState<string>('');
  const [loading, setLoading] = useState<boolean>(false);
  const [checked, setChecked] = useState<boolean>(false);
 
  const onSubmit = useCallback(async (evt: FormEvent) => {
    evt.preventDefault();
    setChecked(false);
    setLoading(true);
    setError('');
    try {
      await loginWithGoogle(user);
      await axios.post('/api/requests', { subjects, description, org: org?.id });
      setChecked(true);
    } catch (err) {
      setChecked(false);
      setLoading(false);
      setError(`Hmm, it looks like we hit a snag. ${(err as Error).message}`);
    }
  }, [user, subjects, description, org?.id]);
  
  return (
    <div className='wrapper'>
      <header>
        <Title>Request a free volunteer tutor</Title>
        <p>Simply fill out the form below and you’ll be matched with a tutor.</p>
      </header>
      <form onSubmit={onSubmit}>
        <Loader active={loading} checked={checked} />
        <div className='section'>
          <TextField
            label='Your phone number'
            value={user.phone ? user.phone : undefined}
            onChange={(e) => {
              const phone = e.currentTarget.value;
              setUser((p) => new User({ ...p, phone }))
            }}
            className='field'
            type='tel'
            outlined
            required={org ? org.profiles.includes('phone') : false}
          />
        </div>
        <hr />
        <div className='section'>
          <SubjectSelect
            label='What would you like to learn?'
            placeholder={t('common:subjects-placeholder')}
            value={subjects}
            onChange={setSubjects}
            className='field'
            required={org ? org.profiles.includes('subjects') : true}
            outlined
          />
          <AvailabilitySelect
            className='field'
            label='When are you available?'
            value={user.availability}
            onChange={(availability) => setUser((p) => new User({ ...p, availability }))}
            required={org ? org.profiles.includes('availability') : true}
            outlined
          />
          <TextField
            label='What specifically do you need help with?'
            placeholder='Ex: I’m really struggling with finding definite integrals in AP Calculus BC and we’ve got a test in two weeks.'
            value={description}
            onChange={(e) => setDescription(e.currentTarget.value)}
            className='field'
            required
            outlined
            rows={3}
            textarea
          />
          <Button
            disabled={loading}
            className='button'
            label='Login and request'
            type='submit'
            raised
            google
            arrow
          />
          {!!error && (
            <div data-cy='error' className='error'>
              {t('user3rd:error', { error })}
            </div>
          )}
        </div>
      </form>
      <style jsx>{`
        .wrapper {
          max-width: 768px;
          margin: 0 auto;
          padding: 48px 24px;
        }

        header {
          text-align: center;
          margin: 0 0 48px;
        }
       
        header p {
          color: var(--accents-5);
        }

        form {
          border: 1px solid var(--accents-2);
          border-radius: 8px;
          position: relative;
        }

        .section {
          margin: 16px;
        }

        hr {
          border: none;
          border-top: 1px solid var(--accents-2);
          margin: 8px 0;
        }

        :global(html:not(.dark)) form {
          box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
        }

        form :global(.field) {
          margin: 8px 0;
          width: 100%;
          display: inline-flex;
        }

        form :global(.field):first-child {
          margin-top: 0;
        }

        form :global(.field):last-child {
          margin-bottom: 0;
        }

        form :global(.field):global(.mdc-select__anchor) {
          width: 100%;
        }

        form :global(.field):global(.mdc-select > .mdc-menu > .mdc-list > .mdc-list-item) {
          white-space: nowrap;
        }

        form :global(.field):not(:global(.mdc-menu-surface--anchor)) textarea {
          min-height: 47px;
        }

        form :global(.field) + :global(.mdc-text-field-helper-line) {
          margin: -6px 0 8px;
        }

        form :global(.field) + :global(.mdc-text-field-helper-line) p {
          font-weight: 450;
          margin: 0;
        }

        form :global(.field) textarea {
          resize: vertical;
        }

        form :global(.button) {
          margin-top: 8px;
          width: 100%;
        }

        .error {
          color: var(--error);
          font-weight: 450;
          font-size: 12px;
          margin-top: 16px;
          text-align: initial;
        }
      `}</style>
    </div>
  );
}