diff options
author | 2024-08-11 13:18:04 -0700 | |
---|---|---|
committer | 2024-08-11 13:18:04 -0700 | |
commit | a9ec4cdbdab6f8026128728c34afa3150663a18b (patch) | |
tree | 093a91e2b05dec5a27dc373b71a038ebde9ee433 /frontend/src/components/NewUserForm | |
parent | 32c8b0b24e868505eb46b3dbe2f3e29d467df2e4 (diff) | |
download | ibd-trader-a9ec4cdbdab6f8026128728c34afa3150663a18b.tar.gz ibd-trader-a9ec4cdbdab6f8026128728c34afa3150663a18b.tar.zst ibd-trader-a9ec4cdbdab6f8026128728c34afa3150663a18b.zip |
Add new-user screen
Diffstat (limited to 'frontend/src/components/NewUserForm')
-rw-r--r-- | frontend/src/components/NewUserForm/NewUserForm.tsx | 171 | ||||
-rw-r--r-- | frontend/src/components/NewUserForm/styles.module.css | 13 |
2 files changed, 184 insertions, 0 deletions
diff --git a/frontend/src/components/NewUserForm/NewUserForm.tsx b/frontend/src/components/NewUserForm/NewUserForm.tsx new file mode 100644 index 0000000..779a49f --- /dev/null +++ b/frontend/src/components/NewUserForm/NewUserForm.tsx @@ -0,0 +1,171 @@ +"use client"; + +import { User } from "@/api/gen/idb/user/v1/user_pb"; +import { ChangeEventHandler, FormEventHandler, MouseEventHandler, useEffect, useState } from "react"; +import { + RequestBody as VerifyRequestBody, + ResponseBody as VerifyResponseBody, +} from "@/app/api/new-user/verify-username/types"; +import { + RequestBody as SubmitRequestBody, + ResponseBody as SubmitResponseBody, +} from "@/app/api/new-user/add-ibd-creds/types"; + +import styles from "./styles.module.css"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faSpinner } from "@fortawesome/free-solid-svg-icons"; + +export default function NewUserForm() { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [isUsernameValid, setIsUsernameValid] = useState(false); + const [isSubmittingUsername, setIsSubmittingUsername] = useState(false); + const [isSubmittingForm, setIsSubmittingForm] = useState(false); + const [isCheckingCreds, setIsCheckingCreds] = useState(false); + const [error, setError] = useState<string | null>(null); + + const handleUsernameSubmit: FormEventHandler<HTMLFormElement> = (e) => { + e.preventDefault(); + setIsSubmittingUsername(true); + setError(null); + + const body: VerifyRequestBody = { username }; + fetch("/api/new-user/verify-username", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }).then((res) => res.json()) + .then((data) => { + const { exists } = VerifyResponseBody.parse(data); + if (exists) { + setIsUsernameValid(true); + } else { + setError("Username does not exist"); + } + setIsSubmittingUsername(false); + }); + }; + const handleFormSubmit: FormEventHandler<HTMLFormElement> = (e) => { + e.preventDefault(); + setIsSubmittingForm(true); + setError(null); + + const body: SubmitRequestBody = { username, password }; + fetch("/api/new-user/add-ibd-creds", { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }).then((res) => res.json()) + .then((data) => { + const {} = SubmitResponseBody.parse(data); + setIsSubmittingForm(false); + setIsCheckingCreds(true); + + return fetch("/api/new-user/check-ibd-creds", { + method: "PUT", + }); + }) + .then((res) => { + if (res.status === 200) { + window.location.href = "/dashboard"; + } else { + setError("Invalid credentials"); + setIsCheckingCreds(false); + } + }); + }; + + const handleBack: MouseEventHandler<HTMLButtonElement> = () => { + setIsUsernameValid(false); + setPassword(""); + setError(null); + }; + + return ( + <div className="flex items-center justify-center min-h-screen bg-gray-100"> + <div className="w-full max-w-md p-8 space-y-4 bg-white rounded-lg shadow-lg"> + <h1 className="text-2xl font-bold text-center">Set Up Your Account</h1> + + <form onSubmit={isUsernameValid ? handleFormSubmit : handleUsernameSubmit}> + <div className="mb-4"> + <label htmlFor="username" className="block text-sm font-medium text-gray-700"> + Username + </label> + <input + type="text" + id="username" + value={username} + onChange={(e) => setUsername(e.target.value)} + className={`w-full px-3 py-2 mt-1 border ${ + isUsernameValid ? "border-gray-300 bg-gray-200" : "border-gray-300" + } rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500`} + required + disabled={isUsernameValid} + /> + {error && !isUsernameValid && ( + <p className="mt-2 text-sm text-red-600">{error}</p> + )} + </div> + {!isUsernameValid && ( + <div className="flex justify-center"> + <button + type="submit" + className="px-4 py-2 text-white bg-indigo-600 rounded-md shadow-sm hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" + disabled={isSubmittingUsername} + > + {isSubmittingUsername ? <FontAwesomeIcon icon={faSpinner} spin /> : "Next"} + </button> + </div> + )} + </form> + + {isUsernameValid && ( + <form onSubmit={handleFormSubmit}> + <div className={styles.passwordForm}> + <div className="mb-4"> + <label htmlFor="password" className="block text-sm font-medium text-gray-700"> + Password + </label> + <input + type="password" + id="password" + value={password} + onChange={(e) => setPassword(e.target.value)} + className="w-full px-3 py-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + required + /> + </div> + {error && isUsernameValid && ( + <p className="mt-2 text-sm text-red-600">{error}</p> + )} + <div className="flex justify-between"> + <button + type="button" + onClick={handleBack} + className="px-4 py-2 text-white bg-gray-600 rounded-md shadow-sm hover:bg-gray-700 focus:ring-2 focus:ring-gray-500 focus:ring-offset-2" + > + Back + </button> + <button + type="submit" + className="px-4 py-2 text-white bg-indigo-600 rounded-md shadow-sm hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" + disabled={isSubmittingForm} + > + {isSubmittingForm ? ( + <p><FontAwesomeIcon icon={faSpinner} spin /> Setting Credentials...</p> + ) : isCheckingCreds ? ( + <p><FontAwesomeIcon icon={faSpinner} spin /> Checking Credentials...</p> + ) : "Submit"} + </button> + </div> + </div> + </form> + )} + </div> + </div> + ); +} diff --git a/frontend/src/components/NewUserForm/styles.module.css b/frontend/src/components/NewUserForm/styles.module.css new file mode 100644 index 0000000..a2ce597 --- /dev/null +++ b/frontend/src/components/NewUserForm/styles.module.css @@ -0,0 +1,13 @@ +@keyframes smooth-appear { + to { + opacity: 1; + transform: translateY(0); + } +} + +.passwordForm { + margin-top: 1rem; + transform: translateY(-5rem); + opacity: 0; + animation: smooth-appear 1s ease forwards; +} |