From 5e98add0a51e20df973638ca0de048e77b380eb1 Mon Sep 17 00:00:00 2001 From: Serena Li Date: Fri, 18 Aug 2023 00:12:50 -0700 Subject: [PATCH] register page almost done --- frontend2/.prettierrc.json | 4 +- frontend2/package-lock.json | 85 ++- frontend2/package.json | 3 +- frontend2/src/components/elements/Button.tsx | 15 +- .../src/components/elements/FormError.tsx | 17 + .../src/components/elements/FormLabel.tsx | 20 + frontend2/src/components/elements/Input.tsx | 45 +- .../src/components/elements/SelectMenu.tsx | 92 ++-- frontend2/src/components/sidebar/index.tsx | 33 +- frontend2/src/index.css | 2 +- frontend2/src/utils/apiTypes.ts | 505 +++++++++--------- frontend2/src/utils/utilTypes.ts | 1 + frontend2/src/views/Register.tsx | 170 ++++-- frontend2/tailwind.config.js | 1 + 14 files changed, 608 insertions(+), 385 deletions(-) create mode 100644 frontend2/src/components/elements/FormError.tsx create mode 100644 frontend2/src/components/elements/FormLabel.tsx create mode 100644 frontend2/src/utils/utilTypes.ts diff --git a/frontend2/.prettierrc.json b/frontend2/.prettierrc.json index 0967ef424..b4bfed357 100644 --- a/frontend2/.prettierrc.json +++ b/frontend2/.prettierrc.json @@ -1 +1,3 @@ -{} +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/frontend2/package-lock.json b/frontend2/package-lock.json index 2c1fb3793..b55e978c2 100644 --- a/frontend2/package-lock.json +++ b/frontend2/package-lock.json @@ -38,7 +38,8 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.32.2", "js-cookie": "^3.0.5", - "prettier": "2.8.8", + "prettier": "3.0.2", + "prettier-plugin-tailwindcss": "^0.5.3", "react-scripts": "5.0.1", "tailwindcss": "^3.3.2", "typescript": "^4.9.5" @@ -15569,20 +15570,92 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", + "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.3.tgz", + "integrity": "sha512-M5K80V21yM+CTm/FEFYRv9/9LyInYbCSXpIoPAKMm8zy89IOwdiA2e4JVbcO7tvRtAQWz32zdj7/WKcsmFyAVg==", + "dev": true, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/frontend2/package.json b/frontend2/package.json index 0862dd98a..e76e3d562 100644 --- a/frontend2/package.json +++ b/frontend2/package.json @@ -61,7 +61,8 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.32.2", "js-cookie": "^3.0.5", - "prettier": "2.8.8", + "prettier": "3.0.2", + "prettier-plugin-tailwindcss": "^0.5.3", "react-scripts": "5.0.1", "tailwindcss": "^3.3.2", "typescript": "^4.9.5" diff --git a/frontend2/src/components/elements/Button.tsx b/frontend2/src/components/elements/Button.tsx index 21c6b51c3..85f910cb8 100644 --- a/frontend2/src/components/elements/Button.tsx +++ b/frontend2/src/components/elements/Button.tsx @@ -5,6 +5,8 @@ interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> { variant?: string; label?: string; iconName?: IconName; + fullWidth?: boolean; + className?: string; } const variants: Record = { @@ -13,21 +15,24 @@ const variants: Record = { }; const Button: React.FC = ({ - variant, + variant = "", label, iconName, + fullWidth = false, + className = "", ...rest }) => { - variant = variant ?? ""; - const variantStyle = variants[variant]; + const variantStyle = `${variants[variant]} ${ + fullWidth ? "w-full" : "" + } ${className}`; return ( ); diff --git a/frontend2/src/components/elements/FormError.tsx b/frontend2/src/components/elements/FormError.tsx new file mode 100644 index 000000000..f8c817415 --- /dev/null +++ b/frontend2/src/components/elements/FormError.tsx @@ -0,0 +1,17 @@ +import React from "react"; + +const FormError: React.FC<{ message?: string; className?: string }> = ({ + message, + className, +}) => { + return ( + + {message ?? "This field is invalid."} + + ); +}; + +export default FormError; diff --git a/frontend2/src/components/elements/FormLabel.tsx b/frontend2/src/components/elements/FormLabel.tsx new file mode 100644 index 000000000..875fdbea2 --- /dev/null +++ b/frontend2/src/components/elements/FormLabel.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +const FormLabel: React.FC<{ + label?: string; + required?: boolean; + className?: string; +}> = ({ label, required = false, className }) => { + return ( + + {label} + {required && *} + + ); +}; + +export default FormLabel; diff --git a/frontend2/src/components/elements/Input.tsx b/frontend2/src/components/elements/Input.tsx index 34b3e4710..0489d7544 100644 --- a/frontend2/src/components/elements/Input.tsx +++ b/frontend2/src/components/elements/Input.tsx @@ -1,35 +1,36 @@ import React, { forwardRef } from "react"; +import FormError from "./FormError"; +import FormLabel from "./FormLabel"; interface InputProps extends React.ComponentPropsWithoutRef<"input"> { label?: string; required?: boolean; + className?: string; + errorMessage?: string; } const Input = forwardRef(function Input( - { label, required = false, ...rest }, - ref + { label, required = false, className = "", errorMessage, ...rest }, + ref, ) { + const invalid = errorMessage !== undefined; return ( -
- {label !== undefined && ( - - )} -
- -
+
+
); }); diff --git a/frontend2/src/components/elements/SelectMenu.tsx b/frontend2/src/components/elements/SelectMenu.tsx index 304885cd6..8ba46b926 100644 --- a/frontend2/src/components/elements/SelectMenu.tsx +++ b/frontend2/src/components/elements/SelectMenu.tsx @@ -1,6 +1,8 @@ -import React, { useMemo, useState } from "react"; -import { Listbox } from "@headlessui/react"; -import Icon, { type IconName } from "./Icon"; +import React, { Fragment, useMemo, useState } from "react"; +import { Listbox, Transition } from "@headlessui/react"; +import Icon from "./Icon"; +import FormError from "./FormError"; +import FormLabel from "./FormLabel"; interface SelectMenuProps { options: Array<{ value: T; label: string }>; @@ -8,6 +10,8 @@ interface SelectMenuProps { required?: boolean; value?: T; placeholder?: string; + className?: string; + errorMessage?: string; onChange?: (value: T) => void; } @@ -17,46 +21,68 @@ function SelectMenu({ options, value, placeholder, + className = "", + errorMessage, onChange, }: SelectMenuProps): JSX.Element { const valueToLabel = useMemo( () => new Map(options.map((option) => [option.value, option.label])), - [options] + [options], ); + const invalid = errorMessage !== undefined; return ( -
+
- {label !== undefined && ( - - - {label} - {required && *} +
+ {label !== undefined && ( + + + + )} + + + {value === undefined ? placeholder : valueToLabel.get(value)} - - )} - - - {value === undefined ? placeholder : valueToLabel.get(value)} - -
- -
-
- - {options.map((option) => ( - - {option.label} - - - - - ))} - + +
+ + + + {options.map((option) => ( + +
{option.label}
+ + + +
+ ))} +
+
+
+ {invalid && }
); } diff --git a/frontend2/src/components/sidebar/index.tsx b/frontend2/src/components/sidebar/index.tsx index ae25e3862..b6fbc28b0 100644 --- a/frontend2/src/components/sidebar/index.tsx +++ b/frontend2/src/components/sidebar/index.tsx @@ -1,17 +1,6 @@ import React, { useContext } from "react"; import SidebarSection from "./SidebarSection"; import SidebarItem from "./SidebarItem"; -import { - ClipboardDocumentIcon, - HomeIcon, - MapIcon, - TrophyIcon, - ChartBarIcon, - ClockIcon, - UserGroupIcon, - ArrowUpTrayIcon, - PlayCircleIcon, -} from "@heroicons/react/24/outline"; import { EpisodeContext } from "../../contexts/EpisodeContext"; interface SidebarProps { @@ -26,52 +15,48 @@ const Sidebar: React.FC = ({ collapsed }) => { return collapsed ? null : (
+ } - text="Home" - linkTo={`${linkBase}home`} - /> - } + iconName="map" text="Quick Start" linkTo={`${linkBase}quickstart`} /> } + iconName="clipboard_document" text="Resources" linkTo={`${linkBase}resources`} /> } + iconName="trophy" text="Tournaments" linkTo={`${linkBase}tournaments`} /> } + iconName="chart_bar" text="Rankings" linkTo={`${linkBase}rankings`} /> } + iconName="clock" text="Queue" linkTo={`${linkBase}queue`} /> } + iconName="user_group" text="My Team" linkTo={`${linkBase}team`} /> } + iconName="arrow_up_tray" text="Submissions" linkTo={`${linkBase}submission`} /> } + iconName="play_circle" text="Scrimmaging" linkTo={`${linkBase}scrimmaging`} /> diff --git a/frontend2/src/index.css b/frontend2/src/index.css index 95142225c..601d99fd1 100644 --- a/frontend2/src/index.css +++ b/frontend2/src/index.css @@ -1,4 +1,4 @@ @tailwind base; @tailwind components; @tailwind utilities; -@import url("https://fonts.googleapis.com/css2?family=Inter&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Inter&family=Josefin+Sans&display=swap"); diff --git a/frontend2/src/utils/apiTypes.ts b/frontend2/src/utils/apiTypes.ts index 6667bd6aa..322e9f8cf 100644 --- a/frontend2/src/utils/apiTypes.ts +++ b/frontend2/src/utils/apiTypes.ts @@ -8,259 +8,260 @@ export enum GenderEnum { export type Gender = `${GenderEnum}`; -const countries = [ - "AF", - "AX", - "AL", - "DZ", - "AS", - "AD", - "AO", - "AI", - "AQ", - "AG", - "AR", - "AM", - "AW", - "AU", - "AT", - "AZ", - "BS", - "BH", - "BD", - "BB", - "BY", - "BE", - "BZ", - "BJ", - "BM", - "BT", - "BO", - "BQ", - "BA", - "BW", - "BV", - "BR", - "IO", - "BN", - "BG", - "BF", - "BI", - "CV", - "KH", - "CM", - "CA", - "KY", - "CF", - "TD", - "CL", - "CN", - "CX", - "CC", - "CO", - "KM", - "CG", - "CD", - "CK", - "CR", - "CI", - "HR", - "CU", - "CW", - "CY", - "CZ", - "DK", - "DJ", - "DM", - "DO", - "EC", - "EG", - "SV", - "GQ", - "ER", - "EE", - "SZ", - "ET", - "FK", - "FO", - "FJ", - "FI", - "FR", - "GF", - "PF", - "TF", - "GA", - "GM", - "GE", - "DE", - "GH", - "GI", - "GR", - "GL", - "GD", - "GP", - "GU", - "GT", - "GG", - "GN", - "GW", - "GY", - "HT", - "HM", - "VA", - "HN", - "HK", - "HU", - "IS", - "IN", - "ID", - "IR", - "IQ", - "IE", - "IM", - "IL", - "IT", - "JM", - "JP", - "JE", - "JO", - "KZ", - "KE", - "KI", - "KW", - "KG", - "LA", - "LV", - "LB", - "LS", - "LR", - "LY", - "LI", - "LT", - "LU", - "MO", - "MG", - "MW", - "MY", - "MV", - "ML", - "MT", - "MH", - "MQ", - "MR", - "MU", - "YT", - "MX", - "FM", - "MD", - "MC", - "MN", - "ME", - "MS", - "MA", - "MZ", - "MM", - "NA", - "NR", - "NP", - "NL", - "NC", - "NZ", - "NI", - "NE", - "NG", - "NU", - "NF", - "KP", - "MK", - "MP", - "NO", - "OM", - "PK", - "PW", - "PS", - "PA", - "PG", - "PY", - "PE", - "PH", - "PN", - "PL", - "PT", - "PR", - "QA", - "RE", - "RO", - "RU", - "RW", - "BL", - "SH", - "KN", - "LC", - "MF", - "PM", - "VC", - "WS", - "SM", - "ST", - "SA", - "SN", - "RS", - "SC", - "SL", - "SG", - "SX", - "SK", - "SI", - "SB", - "SO", - "ZA", - "GS", - "KR", - "SS", - "ES", - "LK", - "SD", - "SR", - "SJ", - "SE", - "CH", - "SY", - "TW", - "TJ", - "TZ", - "TH", - "TL", - "TG", - "TK", - "TO", - "TT", - "TN", - "TR", - "TM", - "TC", - "TV", - "UG", - "UA", - "AE", - "GB", - "UM", - "US", - "UY", - "UZ", - "VU", - "VE", - "VN", - "VG", - "VI", - "WF", - "EH", - "YE", - "ZM", - "ZW", -]; +export const COUNTRIES = { + US: "United States of America", + CA: "Canada", + AU: "Australia", + GB: "United Kingdom", + NL: "Netherlands", + KR: "South Korea", + FR: "France", + AF: "Afghanistan", + AX: "Åland Islands", + AL: "Albania", + DZ: "Algeria", + AS: "American Samoa", + AD: "Andorra", + AO: "Angola", + AI: "Anguilla", + AQ: "Antarctica", + AG: "Antigua and Barbuda", + AR: "Argentina", + AM: "Armenia", + AW: "Aruba", + AT: "Austria", + AZ: "Azerbaijan", + BS: "Bahamas", + BH: "Bahrain", + BD: "Bangladesh", + BB: "Barbados", + BY: "Belarus", + BE: "Belgium", + BZ: "Belize", + BJ: "Benin", + BM: "Bermuda", + BT: "Bhutan", + BO: "Bolivia", + BQ: "Bonaire, Sint Eustatius and Saba", + BA: "Bosnia and Herzegovina", + BW: "Botswana", + BV: "Bouvet Island", + BR: "Brazil", + IO: "British Indian Ocean Territory", + BN: "Brunei", + BG: "Bulgaria", + BF: "Burkina Faso", + BI: "Burundi", + CV: "Cabo Verde", + KH: "Cambodia", + CM: "Cameroon", + KY: "Cayman Islands", + CF: "Central African Republic", + TD: "Chad", + CL: "Chile", + CN: "China", + CX: "Christmas Island", + CC: "Cocos (Keeling) Islands", + CO: "Colombia", + KM: "Comoros", + CG: "Congo", + CD: "Congo (the Democratic Republic of the)", + CK: "Cook Islands", + CR: "Costa Rica", + CI: "Côte d'Ivoire", + HR: "Croatia", + CU: "Cuba", + CW: "Curaçao", + CY: "Cyprus", + CZ: "Czechia", + DK: "Denmark", + DJ: "Djibouti", + DM: "Dominica", + DO: "Dominican Republic", + EC: "Ecuador", + EG: "Egypt", + SV: "El Salvador", + GQ: "Equatorial Guinea", + ER: "Eritrea", + EE: "Estonia", + SZ: "Eswatini", + ET: "Ethiopia", + FK: "Falkland Islands (Malvinas)", + FO: "Faroe Islands", + FJ: "Fiji", + FI: "Finland", + GF: "French Guiana", + PF: "French Polynesia", + TF: "French Southern Territories", + GA: "Gabon", + GM: "Gambia", + GE: "Georgia", + DE: "Germany", + GH: "Ghana", + GI: "Gibraltar", + GR: "Greece", + GL: "Greenland", + GD: "Grenada", + GP: "Guadeloupe", + GU: "Guam", + GT: "Guatemala", + GG: "Guernsey", + GN: "Guinea", + GW: "Guinea-Bissau", + GY: "Guyana", + HT: "Haiti", + HM: "Heard Island and McDonald Islands", + VA: "Holy See", + HN: "Honduras", + HK: "Hong Kong", + HU: "Hungary", + IS: "Iceland", + IN: "India", + ID: "Indonesia", + IR: "Iran", + IQ: "Iraq", + IE: "Ireland", + IM: "Isle of Man", + IL: "Israel", + IT: "Italy", + JM: "Jamaica", + JP: "Japan", + JE: "Jersey", + JO: "Jordan", + KZ: "Kazakhstan", + KE: "Kenya", + KI: "Kiribati", + KW: "Kuwait", + KG: "Kyrgyzstan", + LA: "Laos", + LV: "Latvia", + LB: "Lebanon", + LS: "Lesotho", + LR: "Liberia", + LY: "Libya", + LI: "Liechtenstein", + LT: "Lithuania", + LU: "Luxembourg", + MO: "Macao", + MG: "Madagascar", + MW: "Malawi", + MY: "Malaysia", + MV: "Maldives", + ML: "Mali", + MT: "Malta", + MH: "Marshall Islands", + MQ: "Martinique", + MR: "Mauritania", + MU: "Mauritius", + YT: "Mayotte", + MX: "Mexico", + FM: "Micronesia (Federated States of)", + MD: "Moldova", + MC: "Monaco", + MN: "Mongolia", + ME: "Montenegro", + MS: "Montserrat", + MA: "Morocco", + MZ: "Mozambique", + MM: "Myanmar", + NA: "Namibia", + NR: "Nauru", + NP: "Nepal", + NC: "New Caledonia", + NZ: "New Zealand", + NI: "Nicaragua", + NE: "Niger", + NG: "Nigeria", + NU: "Niue", + NF: "Norfolk Island", + KP: "North Korea", + MK: "North Macedonia", + MP: "Northern Mariana Islands", + NO: "Norway", + OM: "Oman", + PK: "Pakistan", + PW: "Palau", + PS: "Palestine, State of", + PA: "Panama", + PG: "Papua New Guinea", + PY: "Paraguay", + PE: "Peru", + PH: "Philippines", + PN: "Pitcairn", + PL: "Poland", + PT: "Portugal", + PR: "Puerto Rico", + QA: "Qatar", + RE: "Réunion", + RO: "Romania", + RU: "Russia", + RW: "Rwanda", + BL: "Saint Barthélemy", + SH: "Saint Helena, Ascension and Tristan da Cunha", + KN: "Saint Kitts and Nevis", + LC: "Saint Lucia", + MF: "Saint Martin (French part)", + PM: "Saint Pierre and Miquelon", + VC: "Saint Vincent and the Grenadines", + WS: "Samoa", + SM: "San Marino", + ST: "Sao Tome and Principe", + SA: "Saudi Arabia", + SN: "Senegal", + RS: "Serbia", + SC: "Seychelles", + SL: "Sierra Leone", + SG: "Singapore", + SX: "Sint Maarten (Dutch part)", + SK: "Slovakia", + SI: "Slovenia", + SB: "Solomon Islands", + SO: "Somalia", + ZA: "South Africa", + GS: "South Georgia and the South Sandwich Islands", + SS: "South Sudan", + ES: "Spain", + LK: "Sri Lanka", + SD: "Sudan", + SR: "Suriname", + SJ: "Svalbard and Jan Mayen", + SE: "Sweden", + CH: "Switzerland", + SY: "Syria", + TW: "Taiwan", + TJ: "Tajikistan", + TZ: "Tanzania", + TH: "Thailand", + TL: "Timor-Leste", + TG: "Togo", + TK: "Tokelau", + TO: "Tonga", + TT: "Trinidad and Tobago", + TN: "Tunisia", + TR: "Turkey", + TM: "Turkmenistan", + TC: "Turks and Caicos Islands", + TV: "Tuvalu", + UG: "Uganda", + UA: "Ukraine", + AE: "United Arab Emirates", + UM: "United States Minor Outlying Islands", + UY: "Uruguay", + UZ: "Uzbekistan", + VU: "Vanuatu", + VE: "Venezuela", + VN: "Vietnam", + VG: "Virgin Islands (British)", + VI: "Virgin Islands (U.S.)", + WF: "Wallis and Futuna", + EH: "Western Sahara", + YE: "Yemen", + ZM: "Zambia", + ZW: "Zimbabwe", +} as const; + +export type Country = keyof typeof COUNTRIES; -export type Country = (typeof countries)[number]; export interface CreateUserInput { profile: { gender: Gender; diff --git a/frontend2/src/utils/utilTypes.ts b/frontend2/src/utils/utilTypes.ts new file mode 100644 index 000000000..d798e4f97 --- /dev/null +++ b/frontend2/src/utils/utilTypes.ts @@ -0,0 +1 @@ +export type Maybe = T | undefined; diff --git a/frontend2/src/views/Register.tsx b/frontend2/src/views/Register.tsx index e1a2ba2ac..2308ac506 100644 --- a/frontend2/src/views/Register.tsx +++ b/frontend2/src/views/Register.tsx @@ -1,71 +1,161 @@ import React, { useState } from "react"; -import { Auth } from "../utils/api"; +import * as Auth from "../utils/auth"; import Input from "../components/elements/Input"; import Button from "../components/elements/Button"; -import { type SubmitHandler, useForm, Controller } from "react-hook-form"; +import { type SubmitHandler, useForm } from "react-hook-form"; import { useCurrentUser } from "../contexts/CurrentUserContext"; import { - type Gender, GenderEnum, + type Country, type CreateUserInput, + COUNTRIES, } from "../utils/apiTypes"; -import Dropdown from "../components/elements/Dropdown"; +import SelectMenu from "../components/elements/SelectMenu"; +import { type Maybe } from "../utils/utilTypes"; + +const REQUIRED_ERROR_MSG = "This field is required."; const Register: React.FC = () => { - const { setUser } = useCurrentUser(); - const { register, handleSubmit, setValue } = useForm(); - const [gender, setGender] = useState(undefined); + const { login } = useCurrentUser(); + const { + register, + handleSubmit, + setValue, + setError, + clearErrors, + formState: { errors }, + } = useForm(); + const [gender, setGender] = useState>(); + const [country, setCountry] = useState>(); const onSubmit: SubmitHandler = async (data) => { - // https://stackoverflow.com/a/72815057 - console.log("submitted", data); + if (gender === undefined || country === undefined) { + return; + } try { const newUser = await Auth.register(data); - setUser(newUser); + login(newUser); console.log("logged in successfully"); } catch (error) { console.log("failure to register", error); } }; return ( -
- BATTLECODE +
+

+ BATTLECODE +

{/* https://github.com/orgs/react-hook-form/discussions/8622 */}
{ + // validate gender and country + await handleSubmit(onSubmit)(event); + if (gender === undefined) { + setError("profile.gender", { message: REQUIRED_ERROR_MSG }); + } + if (country === undefined) { + setError("profile.country", { message: REQUIRED_ERROR_MSG }); + } + }} + className="m-6 w-11/12 sm:w-[550px] flex flex-col gap-5 rounded-lg bg-gray-100 p-6 shadow-md" > - - - - - + + + +
+ + +
{/* begin profile fields */} - - { - setGender(gender as Gender); - setValue("profile.gender", gender as Gender); +
+ + required + onChange={(newGender) => { + setGender(newGender); + setValue("profile.gender", newGender); + clearErrors("profile.gender"); + }} + errorMessage={errors.profile?.gender?.message} + value={gender} + label="Gender identity" + placeholder="Select gender" + options={[ + { value: GenderEnum.FEMALE, label: "Female" }, + { value: GenderEnum.MALE, label: "Male" }, + { value: GenderEnum.NONBINARY, label: "Non-binary" }, + { + value: GenderEnum.SELF_DESCRIBED, + label: "Prefer to self describe", + }, + { value: GenderEnum.RATHER_NOT_SAY, label: "Rather not say" }, + ]} + /> + +
+ + required + onChange={(newCountry) => { + setCountry(newCountry); + setValue("profile.country", newCountry); + clearErrors("profile.country"); }} - value={gender} - label="Gender" - placeholder="select" - options={[ - { value: GenderEnum.FEMALE, label: "Female" }, - { value: GenderEnum.MALE, label: "Male" }, - { value: GenderEnum.NONBINARY, label: "Non-binary" }, - { - value: GenderEnum.SELF_DESCRIBED, - label: "Prefer to self describe", - }, - { value: GenderEnum.RATHER_NOT_SAY, label: "Rather not say" }, - ]} + errorMessage={errors.profile?.country?.message} + value={country} + label="Country" + placeholder="Select country" + options={Object.entries(COUNTRIES).map(([code, name]) => ({ + value: code as Country, + label: name, + }))} + /> +