aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components/NewUserForm
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2024-08-11 13:18:04 -0700
committerGravatar Anshul Gupta <ansg191@anshulg.com> 2024-08-11 13:18:04 -0700
commita9ec4cdbdab6f8026128728c34afa3150663a18b (patch)
tree093a91e2b05dec5a27dc373b71a038ebde9ee433 /frontend/src/components/NewUserForm
parent32c8b0b24e868505eb46b3dbe2f3e29d467df2e4 (diff)
downloadibd-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.tsx171
-rw-r--r--frontend/src/components/NewUserForm/styles.module.css13
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;
+}