diff options
| -rw-r--r-- | src/App.tsx | 2 | ||||
| -rw-r--r-- | src/pages/FormPage/FormPage.tsx | 41 | ||||
| -rw-r--r-- | src/pages/NotFound.tsx | 26 | 
3 files changed, 61 insertions, 8 deletions
| diff --git a/src/App.tsx b/src/App.tsx index 5430e40..70e0b11 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import { PropagateLoader } from "react-spinners";  import { CSSTransition, TransitionGroup } from "react-transition-group";  import globalStyles from "./globalStyles"; +import NotFound from "./pages/NotFound";  const LandingPage = React.lazy(() => import("./pages/LandingPage"));  const FormPage = React.lazy(() => import("./pages/FormPage/FormPage")); @@ -41,6 +42,7 @@ function Routing(): JSX.Element {      return (          <Routes location={location}>              {renderedRoutes} +            <Route path="*" element={<NotFound message={"404: This page does not exist"}/>}/>          </Routes>      );  } diff --git a/src/pages/FormPage/FormPage.tsx b/src/pages/FormPage/FormPage.tsx index 9f74410..05b51c0 100644 --- a/src/pages/FormPage/FormPage.tsx +++ b/src/pages/FormPage/FormPage.tsx @@ -1,9 +1,11 @@  /** @jsx jsx */ +/** @jsxFrag */  import {jsx, css} from "@emotion/react";  import React, {createRef, SyntheticEvent, useEffect, useState} from "react";  import {useParams} from "react-router";  import {PropagateLoader} from "react-spinners"; +import {AxiosError} from "axios";  import HeaderBar from "../../components/HeaderBar";  import RenderedQuestion from "../../components/Question"; @@ -19,6 +21,7 @@ import handleSubmit, {FormState} from "./submit";  import Navigation from "./Navigation";  import Success from "./SuccessPage";  import ErrorPage from "./ErrorPage"; +import NotFound from "../NotFound";  export type RefMapType =  Map<string, React.RefObject<RenderedQuestion>>; @@ -49,24 +52,47 @@ const closedHeaderStyles = css`    }  `; +enum LoadingState { +    Pending, +    Found, +    Missing +} +  function FormPage(): JSX.Element {      const {id} = useParams<{id: string}>();      const [form, setForm] = useState<Form>(); +    const [formLoading, setFormLoading] = useState<LoadingState>(LoadingState.Pending);      const [state, setState] = useState<string>(FormState.INITIAL);      const OAuthRef = createRef<HTMLDivElement>();      useEffect(() => { -        // This can't be null due to the routing to get here +        // ID can't be null due to the routing to get here          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion          getForm(id!).then(form => {              setForm(form); +            setFormLoading(LoadingState.Found); +        }).catch((error: AxiosError) => { +            if (error.response?.status === 404) { +                setFormLoading(LoadingState.Missing); +                return; +            } + +            throw error;          });      }, []); +    switch (formLoading) { +        case LoadingState.Pending: +            return <Loading/>; +        case LoadingState.Missing: +            return <NotFound message={"Could not find a matching form. Make sure the requested form exists and is open."}/>; +    } +      if (!form) { -        return <Loading/>; +        // This should be an impossible state +        throw Error("Form was not set despite loading state being set to found.");      }      const refMap: RefMapType = new Map(); @@ -88,9 +114,6 @@ function FormPage(): JSX.Element {              <div css={closedHeaderStyles}>This form is now closed. You will not be able to submit your response.</div>;      } -    // FIXME: Remove this ignore -    // eslint-disable-next-line @typescript-eslint/ban-ts-comment -    // @ts-ignore      const questions: RenderedQuestion[] = form.questions.map((question, index) => {          const questionRef = createRef<RenderedQuestion>();          refMap.set(question.id, questionRef); @@ -102,7 +125,7 @@ function FormPage(): JSX.Element {              key={index + Date.now()}              selfRef={questionRef}              ref={questionRef} -        />; +        /> as unknown as RenderedQuestion; // Annotations for JSX elements resolve to a generic ReactElement      });      switch (state) { @@ -133,8 +156,10 @@ function FormPage(): JSX.Element {              <div>                  <form id="form" onSubmit={handler} css={[formStyles, unselectable]}> -                    {closed_header} -                    {questions} +                    <> +                        {closed_header} +                        {questions} +                    </>                  </form>                  <Navigation form_state={open} scopes={scopes}/>              </div> diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx new file mode 100644 index 0000000..6947a38 --- /dev/null +++ b/src/pages/NotFound.tsx @@ -0,0 +1,26 @@ +/** @jsx jsx */ +import {jsx, css} from "@emotion/react"; +import {Link} from "react-router-dom"; + +import HeaderBar from "../components/HeaderBar"; +import {mainTextStyles, navigationStyles, returnButtonStyles, unselectable} from "../commonStyles"; + +interface NotFoundProps { +    message: string +} + + +/** Simple 404 page. */ +export default function NotFound(props: NotFoundProps): JSX.Element { +    return <div> +        <HeaderBar/> +        <div css={css`width: 80%; margin: auto;`}> +            <div css={mainTextStyles}> +                <p>{props.message}</p> +            </div> +            <div css={[unselectable, navigationStyles]}> +                <Link css={returnButtonStyles} to="/">Return Home</Link> +            </div> +        </div> +    </div>; +} | 
