hensei-web/components/auth/SignupModal/index.tsx
Justin Edmund 1c2a1b6bb4
Deploy organization and bug fixes (#299)
* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Added World Series to weapon series empty state (#293)

* Push 2023/03 updates to main (#292)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Add World series to empty state

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Enables advanced filters in collections (#289)

* Add skeleton of FilterModal

* Install react-slider from Radix

* Move AccountModal styles to more generic place

* Make generic TableField and move styles

This is so we have a base for other table rows that use different interactive elements

* Implement custom Slider component

This inherits from Radix's Slider

* Implement SliderTableField

* Implemented SwitchTableField

* Change enabled switch color

* Implement InputTableField

* Update modal skeleton

* Added localizations for Advanced filters

* Update styles for various components

Added some new colors and fixed spacing

* Added value reporting and fixed a cycle error

* Added default values, clearing filters, etc

* Default values
* Ability to clear filters
* Receiving values from components

* Fix maximum cycle depth exceeded error

* Update TableFields to not error

Also optional value is required

* Create FilterSet.d.ts

* Send filtersets to FilterModal

This sends the default filterset and the user's filterset to the filter modal.

The default filterset is used when resetting all filters. The users filterset is used so that it is populated with the user's values when they open the modal

* Add new localizations

* Change types and add default filterset object

* Add fast-deep-equal package

* Change value in table fields

* Input table fields need to be able to be empty
* Slider table fields should default to 0 if value isn't provided

* Set width of Select in table field in Filter dialog

* Add style for filter button with filters active

* Swap to using selects for some boolean fields

Charge Attack, Full Auto, and Auto Guard are not boolean values since the user can select (and the default should be) to show both on and off values. We swap to using a SelectTableField here to represent this difference.

We also added logic for Full Auto and Auto Guard fields since they are tied together in some cases (you can't show Auto Guard teams that have Full Auto disabled)

* Populate values from defaultFilterSet

* Update how we save and propagate filters

We save filterset in a local state, because the FilterBar will send it down to us from cookies.

We then set each individual property from that filter set.

We set inputs to have a placeholder, as max buttons and max turns could not be set (null). Then, we only send those fields when they have a value provided by the user.

* Remove default filterset

This was moved to a utils/ file

* Propagate filters from modal

This updates how we handle filter propagation to accommodate the advanced ones. The icon lights up when filters are active.

* Implement advanced filters on Teams page

* Add skeleton of FilterModal

* Make generic TableField and move styles

This is so we have a base for other table rows that use different interactive elements

* Implement custom Slider component

This inherits from Radix's Slider

* Implement SliderTableField

* Implemented SwitchTableField

* Implement InputTableField

* Update modal skeleton

* Added localizations for Advanced filters

* Update styles for various components

Added some new colors and fixed spacing

* Added value reporting and fixed a cycle error

* Added default values, clearing filters, etc

* Default values
* Ability to clear filters
* Receiving values from components

* Fix maximum cycle depth exceeded error

* Update TableFields to not error

Also optional value is required

* Create FilterSet.d.ts

* Send filtersets to FilterModal

This sends the default filterset and the user's filterset to the filter modal.

The default filterset is used when resetting all filters. The users filterset is used so that it is populated with the user's values when they open the modal

* Add new localizations

* Change types and add default filterset object

* Change value in table fields

* Input table fields need to be able to be empty
* Slider table fields should default to 0 if value isn't provided

* Set width of Select in table field in Filter dialog

* Add style for filter button with filters active

* Swap to using selects for some boolean fields

Charge Attack, Full Auto, and Auto Guard are not boolean values since the user can select (and the default should be) to show both on and off values. We swap to using a SelectTableField here to represent this difference.

We also added logic for Full Auto and Auto Guard fields since they are tied together in some cases (you can't show Auto Guard teams that have Full Auto disabled)

* Populate values from defaultFilterSet

* Update how we save and propagate filters

We save filterset in a local state, because the FilterBar will send it down to us from cookies.

We then set each individual property from that filter set.

We set inputs to have a placeholder, as max buttons and max turns could not be set (null). Then, we only send those fields when they have a value provided by the user.

* Remove default filterset

This was moved to a utils/ file

* Propagate filters from modal

This updates how we handle filter propagation to accommodate the advanced ones. The icon lights up when filters are active.

* GridRep adjustments

* Properly unset mainhand when cells get reused and the new team doesnt have one
* Slightly better styling to make the grid more correct

* Fix bad merge

* Add advanced filter support to saved and profile pages

* Fix auto guard text

* Ensure fetchTeams callback is updated with filters

* Add auto guard icon to GridRep

* Disable max buttons and turns

* Fix build errors

* Organize components (#298)

* Deploy advanced filters (#297)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Added World Series to weapon series empty state (#293)

* Push 2023/03 updates to main (#292)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Add World series to empty state

* Added items from 2023/03 Legfest and 2023/03/30 update (#290)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Missed items (#291)

* Added avatars (#286)

* Deploy #287 (#288)

* Added avatars

* Added content from the 2023/03/22 update (#287)

* Added avatars (#286)

* Added localizations

* Added update, changed CSS

* Add logic for showing Lucifer uncap and 250 art

* Added new weapon series

* Added updates

* Add more items

* Enables advanced filters in collections (#289)

* Add skeleton of FilterModal

* Install react-slider from Radix

* Move AccountModal styles to more generic place

* Make generic TableField and move styles

This is so we have a base for other table rows that use different interactive elements

* Implement custom Slider component

This inherits from Radix's Slider

* Implement SliderTableField

* Implemented SwitchTableField

* Change enabled switch color

* Implement InputTableField

* Update modal skeleton

* Added localizations for Advanced filters

* Update styles for various components

Added some new colors and fixed spacing

* Added value reporting and fixed a cycle error

* Added default values, clearing filters, etc

* Default values
* Ability to clear filters
* Receiving values from components

* Fix maximum cycle depth exceeded error

* Update TableFields to not error

Also optional value is required

* Create FilterSet.d.ts

* Send filtersets to FilterModal

This sends the default filterset and the user's filterset to the filter modal.

The default filterset is used when resetting all filters. The users filterset is used so that it is populated with the user's values when they open the modal

* Add new localizations

* Change types and add default filterset object

* Add fast-deep-equal package

* Change value in table fields

* Input table fields need to be able to be empty
* Slider table fields should default to 0 if value isn't provided

* Set width of Select in table field in Filter dialog

* Add style for filter button with filters active

* Swap to using selects for some boolean fields

Charge Attack, Full Auto, and Auto Guard are not boolean values since the user can select (and the default should be) to show both on and off values. We swap to using a SelectTableField here to represent this difference.

We also added logic for Full Auto and Auto Guard fields since they are tied together in some cases (you can't show Auto Guard teams that have Full Auto disabled)

* Populate values from defaultFilterSet

* Update how we save and propagate filters

We save filterset in a local state, because the FilterBar will send it down to us from cookies.

We then set each individual property from that filter set.

We set inputs to have a placeholder, as max buttons and max turns could not be set (null). Then, we only send those fields when they have a value provided by the user.

* Remove default filterset

This was moved to a utils/ file

* Propagate filters from modal

This updates how we handle filter propagation to accommodate the advanced ones. The icon lights up when filters are active.

* Implement advanced filters on Teams page

* Add skeleton of FilterModal

* Make generic TableField and move styles

This is so we have a base for other table rows that use different interactive elements

* Implement custom Slider component

This inherits from Radix's Slider

* Implement SliderTableField

* Implemented SwitchTableField

* Implement InputTableField

* Update modal skeleton

* Added localizations for Advanced filters

* Update styles for various components

Added some new colors and fixed spacing

* Added value reporting and fixed a cycle error

* Added default values, clearing filters, etc

* Default values
* Ability to clear filters
* Receiving values from components

* Fix maximum cycle depth exceeded error

* Update TableFields to not error

Also optional value is required

* Create FilterSet.d.ts

* Send filtersets to FilterModal

This sends the default filterset and the user's filterset to the filter modal.

The default filterset is used when resetting all filters. The users filterset is used so that it is populated with the user's values when they open the modal

* Add new localizations

* Change types and add default filterset object

* Change value in table fields

* Input table fields need to be able to be empty
* Slider table fields should default to 0 if value isn't provided

* Set width of Select in table field in Filter dialog

* Add style for filter button with filters active

* Swap to using selects for some boolean fields

Charge Attack, Full Auto, and Auto Guard are not boolean values since the user can select (and the default should be) to show both on and off values. We swap to using a SelectTableField here to represent this difference.

We also added logic for Full Auto and Auto Guard fields since they are tied together in some cases (you can't show Auto Guard teams that have Full Auto disabled)

* Populate values from defaultFilterSet

* Update how we save and propagate filters

We save filterset in a local state, because the FilterBar will send it down to us from cookies.

We then set each individual property from that filter set.

We set inputs to have a placeholder, as max buttons and max turns could not be set (null). Then, we only send those fields when they have a value provided by the user.

* Remove default filterset

This was moved to a utils/ file

* Propagate filters from modal

This updates how we handle filter propagation to accommodate the advanced ones. The icon lights up when filters are active.

* GridRep adjustments

* Properly unset mainhand when cells get reused and the new team doesnt have one
* Slightly better styling to make the grid more correct

* Fix bad merge

* Add advanced filter support to saved and profile pages

* Fix auto guard text

* Ensure fetchTeams callback is updated with filters

* Add auto guard icon to GridRep

* Disable max buttons and turns

* Fix build errors

* Organize components into folders

* Fix transcendence popover levels

* Remove extra styles

* Add Storybook

* Delete Home.module.css

* Update paths

* Fix popover arrow

* Fix import paths

* Fix scrollbars in search

* Add type to further fix TranscendencePopover

* Add background-size to 1x in hidpiImage mixin

* Fix hovercard scrollbar

* Move components

* Fix ElementToggle on smaller devices

* Change default filterset

Min characters should be 2, not 3
2023-04-12 06:37:41 -07:00

374 lines
9.7 KiB
TypeScript

import React, { useEffect, useState } from 'react'
import { setCookie } from 'cookies-next'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import { AxiosResponse } from 'axios'
import api from '~utils/api'
import { setHeaders } from '~utils/userToken'
import { accountState } from '~utils/accountState'
import Button from '~components/common/Button'
import Input from '~components/common/Input'
import { Dialog, DialogTrigger, DialogClose } from '~components/common/Dialog'
import DialogContent from '~components/common/DialogContent'
import CrossIcon from '~public/icons/Cross.svg'
import './index.scss'
interface Props {
open: boolean
onOpenChange?: (open: boolean) => void
}
interface ErrorMap {
[index: string]: string
username: string
email: string
password: string
passwordConfirmation: string
}
const emailRegex =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
const SignupModal = (props: Props) => {
const router = useRouter()
const { t } = useTranslation('common')
// Set up form states and error handling
const [formValid, setFormValid] = useState(false)
const [errors, setErrors] = useState<ErrorMap>({
username: '',
email: '',
password: '',
passwordConfirmation: '',
})
// States
const [open, setOpen] = useState(false)
// Set up form refs
const usernameInput = React.createRef<HTMLInputElement>()
const emailInput = React.createRef<HTMLInputElement>()
const passwordInput = React.createRef<HTMLInputElement>()
const passwordConfirmationInput = React.createRef<HTMLInputElement>()
const footerRef = React.createRef<HTMLDivElement>()
const form = [
usernameInput,
emailInput,
passwordInput,
passwordConfirmationInput,
]
useEffect(() => {
setOpen(props.open)
}, [props.open])
function register(event: React.FormEvent) {
event.preventDefault()
const body = {
user: {
username: usernameInput.current?.value,
email: emailInput.current?.value,
password: passwordInput.current?.value,
password_confirmation: passwordConfirmationInput.current?.value,
language: router.locale,
},
}
if (formValid)
api.endpoints.users
.create(body)
.then((response) => {
storeCookieInfo(response)
return response.data.id
})
.then((id) => fetchUserInfo(id))
.then((infoResponse) => storeUserInfo(infoResponse))
}
function storeCookieInfo(response: AxiosResponse) {
const resp = response.data
const cookieObj: AccountCookie = {
userId: resp.id,
username: resp.username,
token: resp.token,
}
const expiresAt = new Date()
expiresAt.setDate(expiresAt.getDate() + 60)
setCookie('account', cookieObj, { path: '/', expires: expiresAt })
// Set Axios default headers
setHeaders()
}
function fetchUserInfo(id: string) {
return api.userInfo(id)
}
function storeUserInfo(response: AxiosResponse) {
// Extract the user
const user = response.data
// Set user data in the user cookie
const expiresAt = new Date()
expiresAt.setDate(expiresAt.getDate() + 60)
setCookie(
'user',
{
avatar: {
picture: user.avatar.picture,
element: user.avatar.element,
},
language: user.language,
gender: user.gender,
theme: user.theme,
},
{ path: '/', expires: expiresAt }
)
// Set the user data in the account state
accountState.account.user = {
id: user.id,
username: user.username,
granblueId: '',
avatar: {
picture: user.avatar.picture,
element: user.avatar.element,
},
gender: user.gender,
language: user.language,
theme: user.theme,
}
console.log('Authorizing account...')
accountState.account.authorized = true
setOpen(false)
}
function handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
event.preventDefault()
const fieldName = event.target.name
const value = event.target.value
if (value.length >= 3) {
api.check(fieldName, value).then(
(response) => {
processNameCheck(fieldName, value, response.data.available)
},
(error) => {
console.error(error)
}
)
} else {
validateName(fieldName, value)
}
}
function processNameCheck(
fieldName: string,
value: string,
available: boolean
) {
const newErrors = { ...errors }
if (available) {
// Continue checking for errors
newErrors[fieldName] = ''
setErrors(newErrors)
setFormValid(true)
validateName(fieldName, value)
} else {
newErrors[fieldName] = t('modals.signup.errors.field_in_use', {
field: fieldName,
})
setErrors(newErrors)
setFormValid(false)
}
}
function validateName(fieldName: string, value: string) {
let newErrors = { ...errors }
switch (fieldName) {
case 'username':
if (value.length < 3)
newErrors.username = t('modals.signup.errors.username_too_short')
else if (value.length > 20)
newErrors.username = t('modals.signup.errors.username_too_long')
else newErrors.username = ''
break
case 'email':
newErrors.email = emailRegex.test(value)
? ''
: t('modals.signup.errors.invalid_email')
break
default:
break
}
setFormValid(validateForm(newErrors))
}
function handlePasswordChange(event: React.ChangeEvent<HTMLInputElement>) {
event.preventDefault()
const { name, value } = event.target
let newErrors = { ...errors }
switch (name) {
case 'password':
newErrors.password = passwordInput.current?.value.includes(
usernameInput.current?.value!
)
? t('modals.signup.errors.password_contains_username')
: ''
break
case 'password':
newErrors.password =
value.length < 8 ? t('modals.signup.errors.password_too_short') : ''
break
case 'confirm_password':
newErrors.passwordConfirmation =
passwordInput.current?.value ===
passwordConfirmationInput.current?.value
? ''
: t('modals.signup.errors.passwords_dont_match')
break
default:
break
}
setFormValid(validateForm(newErrors))
}
function validateForm(errors: ErrorMap) {
let valid = true
Object.values(form).forEach(
(input) => input.current?.value.length == 0 && (valid = false)
)
Object.values(errors).forEach(
(error) => error.length > 0 && (valid = false)
)
return valid
}
function openChange(open: boolean) {
setOpen(open)
setErrors({
username: '',
email: '',
password: '',
passwordConfirmation: '',
})
setFormValid(false)
if (props.onOpenChange) props.onOpenChange(open)
}
function onEscapeKeyDown(event: KeyboardEvent) {
setOpen(false)
}
function onOpenAutoFocus(event: Event) {
event.preventDefault()
if (usernameInput.current) usernameInput.current.focus()
}
return (
<Dialog open={open} onOpenChange={openChange}>
<DialogContent
className="Signup"
footerref={footerRef}
onEscapeKeyDown={onEscapeKeyDown}
onOpenAutoFocus={onOpenAutoFocus}
>
<div className="DialogHeader">
<div className="DialogTitle">
<h1>{t('modals.signup.title')}</h1>
</div>
<DialogClose className="DialogClose">
<CrossIcon />
</DialogClose>
</div>
<form className="form" onSubmit={register}>
<div className="Fields">
<Input
className="Bound"
name="username"
placeholder={t('modals.signup.placeholders.username')}
onChange={handleNameChange}
error={errors.username}
ref={usernameInput}
/>
<Input
className="Bound"
name="email"
placeholder={t('modals.signup.placeholders.email')}
onChange={handleNameChange}
error={errors.email}
ref={emailInput}
/>
<Input
className="Bound"
name="password"
placeholder={t('modals.signup.placeholders.password')}
type="password"
onChange={handlePasswordChange}
error={errors.password}
ref={passwordInput}
/>
<Input
className="Bound"
name="confirm_password"
placeholder={t('modals.signup.placeholders.password_confirm')}
type="password"
onChange={handlePasswordChange}
error={errors.passwordConfirmation}
ref={passwordConfirmationInput}
/>
</div>
<div className="DialogFooter" ref={footerRef}>
<div className="Buttons Span">
<Button
contained={true}
disabled={!formValid}
text={t('modals.signup.buttons.confirm')}
/>
</div>
</div>
<p className="terms">
{/* <Trans i18nKey="modals.signup.agreement">
By signing up, I agree to the <Link href="/privacy"><span>Privacy Policy</span></Link><Link href="/usage"><span>Usage Guidelines</span></Link>.
</Trans> */}
</p>
</form>
</DialogContent>
</Dialog>
)
}
export default SignupModal