From a7d361723dc1250692509a5926f60d340eaf1b3d Mon Sep 17 00:00:00 2001 From: Willard Nilges Date: Tue, 13 Feb 2024 23:30:33 -0500 Subject: [PATCH] Fix async/rendering issues with Query form and provide a loading indicator (#25) * switch button to mui * Use onSubmit to prevent blocking UI thanks andy * delete unused imports * Fix Join Form Submit button and phone input * Fix join form, fix nn assign form button * maybe it would make more sense to hardcode the routes --- .env.sample | 2 +- app/api.ts | 2 +- components/Button/Button.module.scss | 12 ---- components/Button/Button.tsx | 57 ------------------- components/JoinForm/JoinForm.module.scss | 5 ++ components/JoinForm/JoinForm.tsx | 45 ++++++++++----- components/Landing/Landing.tsx | 1 - .../NNAssignForm/NNAssignForm.module.scss | 19 ++----- components/NNAssignForm/NNAssignForm.tsx | 12 +++- components/QueryForm/QueryForm.module.scss | 17 ++---- components/QueryForm/QueryForm.tsx | 57 ++++++++++--------- 11 files changed, 89 insertions(+), 140 deletions(-) delete mode 100644 components/Button/Button.module.scss delete mode 100644 components/Button/Button.tsx diff --git a/.env.sample b/.env.sample index e795c9d..62e0ea7 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,4 @@ -NEXT_PUBLIC_MESHDB_URL=http://127.0.0.1:8000/api/v1/ +NEXT_PUBLIC_MESHDB_URL=http://127.0.0.1:8000 # Docker compose environment variables # Set this to true in prod diff --git a/app/api.ts b/app/api.ts index 8445adf..ff6e749 100644 --- a/app/api.ts +++ b/app/api.ts @@ -4,7 +4,7 @@ if (process.env.NEXT_PUBLIC_MESHDB_URL === undefined) { throw new Error('Expected API url environment variable'); } -const API_BASE = new URL(process.env.NEXT_PUBLIC_MESHDB_URL as string); +const API_BASE = new URL(process.env.NEXT_PUBLIC_MESHDB_URL as string + "/api/v1/"); export const JoinFormInput = z.object({ first_name: z.string(), diff --git a/components/Button/Button.module.scss b/components/Button/Button.module.scss deleted file mode 100644 index 504f3e3..0000000 --- a/components/Button/Button.module.scss +++ /dev/null @@ -1,12 +0,0 @@ -.button { - background-color: black; - border-radius: 4px; - color: white; - margin: 30px 0px 30px 0px; - padding: 10px 24px; - border: none; - cursor: pointer; - width: 100%; - font-size: 16px; - outline-offset: 4px; -} diff --git a/components/Button/Button.tsx b/components/Button/Button.tsx deleted file mode 100644 index 37ff710..0000000 --- a/components/Button/Button.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import Link from 'next/link' - -import styles from './Button.module.scss' - -export const makeClass = (...classes: (string | false | undefined | null | 0 | '')[]) => - classes.filter(Boolean).join(' ') - -type ButtonProps = { - /** If provided, will render a link that looks like a button */ - href?: string - icon?: React.ReactNode - children?: React.ReactNode - isSecondary?: boolean - isSmall?: boolean - isLoading?: boolean - /** Override the surface color of the button. Will force the text to #FFFFFF. */ - surfaceColor?: string - /** Override the shadow color of the button */ - shadowColor?: string -} & Omit & React.ComponentProps<'a'>, 'ref'> - -const Button: React.FC = ({ - href, - type = 'button', - icon, - children, - isSecondary, - isSmall, - isLoading, - surfaceColor, - shadowColor, - style, - ...props -}) => { - const sharedProps = { - className: makeClass( - styles.button, - isSecondary && styles.secondary, - isSmall && styles.small, - isLoading && styles.loading, - !children && icon && styles.iconButton, - ), - style: { - ...surfaceColor && { '--override-surface-color': surfaceColor, '--override-text-color': '#FFFFFF' }, - ...shadowColor && { '--override-shadow-color': shadowColor }, - ...style, - }, - children:
{icon}{children}
, - ...props, - } - - return href - ? - : +
+ +
diff --git a/components/Landing/Landing.tsx b/components/Landing/Landing.tsx index 68a9fdf..4b5674a 100644 --- a/components/Landing/Landing.tsx +++ b/components/Landing/Landing.tsx @@ -1,7 +1,6 @@ "use client"; import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; -// import Button from "@/components/Button/Button"; import styles from "./Landing.module.scss"; import { Box, Grid, Typography } from "@mui/material"; diff --git a/components/NNAssignForm/NNAssignForm.module.scss b/components/NNAssignForm/NNAssignForm.module.scss index 97c3064..64726f5 100644 --- a/components/NNAssignForm/NNAssignForm.module.scss +++ b/components/NNAssignForm/NNAssignForm.module.scss @@ -20,19 +20,6 @@ outline-offset: 4px; } -.submitButton { - background-color: black; - border-radius: 4px; - color: white; - margin: 30px 0px 30px 0px; - padding: 10px 24px; - border: none; - cursor: pointer; - width: 100%; - font-size: 16px; - outline-offset: 4px; -} - .formBody button:hover { opacity: 0.8; } @@ -97,3 +84,9 @@ .nnLabel { color: #666; } + +/*FIXME: For the love of god stop duplicating this*/ +.centered { + display: flex; + justify-content: center; +} diff --git a/components/NNAssignForm/NNAssignForm.tsx b/components/NNAssignForm/NNAssignForm.tsx index cb5d827..2f1db24 100644 --- a/components/NNAssignForm/NNAssignForm.tsx +++ b/components/NNAssignForm/NNAssignForm.tsx @@ -1,5 +1,6 @@ "use client"; +import Button from "@mui/material/Button"; import { useFormState } from "react-dom"; import { useFormStatus } from "react-dom"; import { NNAssignFormInput, submitNNAssignForm } from "@/app/api"; @@ -73,7 +74,16 @@ export function NNAssignForm() { - +
+ +

{networkNumber}

diff --git a/components/QueryForm/QueryForm.module.scss b/components/QueryForm/QueryForm.module.scss index d40f083..5ebc7bd 100644 --- a/components/QueryForm/QueryForm.module.scss +++ b/components/QueryForm/QueryForm.module.scss @@ -20,19 +20,6 @@ outline-offset: 4px; } -.submitButton { - background-color: black; - border-radius: 4px; - color: white; - margin: 30px 0px 30px 0px; - padding: 10px 24px; - border: none; - cursor: pointer; - width: 100%; - font-size: 16px; - outline-offset: 4px; -} - .formBody button:hover { opacity: 0.8; } @@ -103,3 +90,7 @@ justify-content: center; } +.centered { + display: flex; + justify-content: center; +} diff --git a/components/QueryForm/QueryForm.tsx b/components/QueryForm/QueryForm.tsx index dd7af99..22b5531 100644 --- a/components/QueryForm/QueryForm.tsx +++ b/components/QueryForm/QueryForm.tsx @@ -1,16 +1,13 @@ -"use client"; +'use client' -import { useFormState } from "react-dom"; -import { useFormStatus } from "react-dom"; -import { QueryFormInput, QueryFormResponse, submitQueryForm } from "@/app/api"; -import { useRouter } from 'next/navigation' -import { ErrorBoundary } from "react-error-boundary"; +import { QueryFormInput, submitQueryForm } from "@/app/api"; +import Button from "@mui/material/Button"; import { toastErrorMessage } from "@/app/utils/toastErrorMessage"; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import Select from 'react-select' -import { useState, useMemo } from "react"; +import { useState, useMemo, FormEvent } from "react"; const options = [ { value: "street_address", label: "Address" }, { value: "email_address", label: "Email" }, @@ -27,19 +24,24 @@ import "ag-grid-community/styles/ag-theme-quartz.css"; // Theme import styles from './QueryForm.module.scss' export function QueryForm() { + const [isLoading, setIsLoading] = useState(false); + const [queryLabel, setQueryLabel] = useState('Select Query Type'); + const [queryResult, setQueryResult] = useState([]); - function parseForm(event: FormData) { + function parseForm(event: FormEvent) { + const formData = new FormData(event.currentTarget) const data: Record< string, string | Blob > = {}; - event.forEach((value, key) => { + formData.forEach((value, key) => { data[key] = value; }); return QueryFormInput.parse(data); } - async function sendForm(event: FormData) { + async function sendForm(event: FormEvent) { + event.preventDefault(); + setIsLoading(true); try { - setQueryComplete(true); const queryForm: QueryFormInput = parseForm(event); let route: string = ''; @@ -62,7 +64,7 @@ export function QueryForm() { break; } console.log(queryForm); - let resp = await submitQueryForm( + const resp = await submitQueryForm( route, queryForm.query_type, queryForm.data, @@ -73,29 +75,23 @@ export function QueryForm() { toast.warning('Query returned no results.', { hideProgressBar: true, }); - return + setIsLoading(false); + return; } + setQueryResult(resp); toast.success('Success!', { hideProgressBar: true, }); - setQueryResult(resp); + setIsLoading(false); } catch (e) { console.log("Could not submit Query Form: "); console.log(e); toastErrorMessage(e); - setQueryComplete(false); + setIsLoading(false); return; } } - const initialState = {}; - const router = useRouter() - const [queryComplete, setQueryComplete] = useState(false); - const [queryType, setQueryType] = useState('select_query_type'); - const [queryLabel, setQueryLabel] = useState('Select Query Type'); - - const [queryResult, setQueryResult] = useState([]); - // Column Definitions: Defines & controls grid columns. const colDefs: ColDef[] = useMemo(() => [ { field: "install_number", headerName: 'Install #', width: 100 }, @@ -136,7 +132,7 @@ const defaultColDef: ColDef = useMemo(() => { return <>
-
+

MeshDB Query

This is for installers to query our database. This is password protected.


@@ -146,7 +142,6 @@ const defaultColDef: ColDef = useMemo(() => { options={options} className={styles.drop} onChange={(selected) => { - selected? setQueryType(selected.value):null; selected? setQueryLabel(selected.label):null; }} /> @@ -154,7 +149,17 @@ const defaultColDef: ColDef = useMemo(() => {
- +
+ +
Double-click to select/expand. Scroll for more!