refactor(auth): migrate login, account, user and error screens to v3
- Login/forgot/reset, create/edit user, profile: Field/Dialog/Toaster shims, colorPalette/button renames, typed hookstate handlers - DeleteDialogue, NotFound, SomethingWentWrong: dialog + centered layout on v3 tokens with legacy copy intact
This commit is contained in:
@@ -3,13 +3,9 @@ import agent from '../api/agent'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Data } from '../models/game'
|
import { Data } from '../models/game'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
Button, Text
|
||||||
AlertDialogBody,
|
|
||||||
AlertDialogContent, AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogOverlay,
|
|
||||||
Button, Icon, Text
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot} from './ui/dialog'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string
|
id: string
|
||||||
@@ -44,34 +40,31 @@ export default function DeleteDialogue({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancelRef = useRef(null)
|
const cancelRef = useRef<HTMLButtonElement | null>(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog isOpen={open} leastDestructiveRef={cancelRef} onClose={handleClose}>
|
<DialogRoot open={open} onOpenChange={(e) => { if (!e.open) handleClose() }} role="alertdialog" initialFocusEl={() => cancelRef.current}>
|
||||||
<AlertDialogOverlay>
|
<DialogContent>
|
||||||
<AlertDialogContent>
|
<DialogHeader fontSize='lg' fontWeight='bold'>Delete?</DialogHeader>
|
||||||
|
<DialogBody>
|
||||||
<AlertDialogHeader fontSize='lg' fontWeight='bold'>Delete?</AlertDialogHeader>
|
Are you sure you would like to delete <strong>{game.title}</strong>?
|
||||||
<AlertDialogBody>
|
</DialogBody>
|
||||||
Are you sure you would like to delete <strong>{game.title}</strong>?
|
<DialogFooter>
|
||||||
</AlertDialogBody>
|
<Button ref={cancelRef} onMouseDown={handleClose}>
|
||||||
<AlertDialogFooter>
|
<Text mt='1'>No</Text>
|
||||||
<Button ref={cancelRef} onMouseDown={handleClose}>
|
</Button>
|
||||||
<Icon name='remove' mr='1' /> <Text mt='1'>No</Text>
|
<Button
|
||||||
</Button>
|
colorPalette='red'
|
||||||
<Button
|
onMouseDown={() => {
|
||||||
colorScheme='red'
|
deleteGame(id)
|
||||||
onMouseDown={() => {
|
}}
|
||||||
deleteGame(id)
|
ml={3}
|
||||||
}}
|
autoFocus
|
||||||
ml={3}
|
>
|
||||||
autoFocus
|
<Text mt='1'>Yes</Text>
|
||||||
>
|
</Button>
|
||||||
<Icon name='checkmark' mr='1' /> <Text mt='1'>Yes</Text>
|
</DialogFooter>
|
||||||
</Button>
|
</DialogContent>
|
||||||
</AlertDialogFooter>
|
</DialogRoot>
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialogOverlay>
|
|
||||||
</AlertDialog>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Checkbox, Text } from '@chakra-ui/react'
|
import { Text } from '@chakra-ui/react'
|
||||||
|
import {Checkbox} from '../ui/checkbox'
|
||||||
import { adminModeToggle, loggedInUser, adminMode } from '../../stateManagement/userState'
|
import { adminModeToggle, loggedInUser, adminMode } from '../../stateManagement/userState'
|
||||||
import { useHookstate } from '@hookstate/core'
|
import { useHookstate } from '@hookstate/core'
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ export default function AdminMode() {
|
|||||||
const admin = useHookstate(adminMode).get()
|
const admin = useHookstate(adminMode).get()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Checkbox size={'lg'} onChange={() => adminModeToggle()} isChecked={admin} isDisabled={!(user.role === 'admin')}>
|
<Checkbox colorPalette="blue" size={'lg'} onCheckedChange={() => adminModeToggle()} checked={admin} disabled={!(user.role === 'admin')}>
|
||||||
<Text mt={'1'}>Administrator Mode</Text>
|
<Text mt={'1'}>Administrator Mode</Text>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { useForm } from 'react-hook-form'
|
|||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
|
||||||
FormLabel,
|
|
||||||
Input,
|
Input,
|
||||||
useColorModeValue, FormErrorMessage, useToast,
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {Field} from '../ui/field'
|
||||||
|
import {useColorModeValue} from '../ui/color-mode'
|
||||||
|
import {toaster} from '../ui/toaster'
|
||||||
import agent from '../../api/agent'
|
import agent from '../../api/agent'
|
||||||
import { IForgotPassword } from '../../models/user'
|
import { IForgotPassword } from '../../models/user'
|
||||||
|
|
||||||
@@ -18,7 +18,6 @@ export default function ForgotPassword() {
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors, isSubmitting, isDirty },
|
formState: { errors, isSubmitting, isDirty },
|
||||||
} = useForm()
|
} = useForm()
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
success: boolean
|
success: boolean
|
||||||
@@ -27,27 +26,25 @@ export default function ForgotPassword() {
|
|||||||
|
|
||||||
const onSubmit = handleSubmit(async (values) => {
|
const onSubmit = handleSubmit(async (values) => {
|
||||||
const message = await agent.Account.forgot(values as IForgotPassword) as Message
|
const message = await agent.Account.forgot(values as IForgotPassword) as Message
|
||||||
toast({
|
toaster.create({
|
||||||
title: 'Email Sent',
|
title: 'Email Sent',
|
||||||
status: 'success',
|
type: 'success',
|
||||||
description: message.data,
|
description: message.data,
|
||||||
isClosable: true,
|
closable: true,
|
||||||
duration: 10000,
|
duration: 10000,
|
||||||
position: 'top'
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<FormControl isInvalid={Boolean(errors.email)} mb={'3'}>
|
<Field label="Email" invalid={Boolean(errors.email)} errorText={errors.email?.type === 'required' ? errors.email.message?.toString() : errors.email?.type === 'pattern' ? 'Invalid Email Address' : undefined} mb={'3'}>
|
||||||
<FormLabel htmlFor="email">Email</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
{...register('email', {
|
{...register('email', {
|
||||||
@@ -55,16 +52,13 @@ export default function ForgotPassword() {
|
|||||||
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<FormErrorMessage>
|
</Field>
|
||||||
{errors.email?.type === 'required' && errors.email.message?.toString()}
|
|
||||||
{errors.email?.type === 'pattern' && 'Invalid Email Address'}
|
|
||||||
</FormErrorMessage>
|
|
||||||
</FormControl>
|
|
||||||
<br/>
|
<br/>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
colorPalette="blue"
|
||||||
disabled={!isDirty}
|
disabled={!isDirty}
|
||||||
isLoading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { login } from '../../stateManagement/userState'
|
|||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
Input, HStack, Center,
|
||||||
FormLabel,
|
|
||||||
Input,
|
|
||||||
useColorModeValue, HStack, Center, FormErrorMessage,
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {Field} from '../ui/field'
|
||||||
|
import {useColorModeValue} from '../ui/color-mode'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
export default function LoginForm() {
|
export default function LoginForm() {
|
||||||
@@ -38,14 +37,13 @@ export default function LoginForm() {
|
|||||||
return (
|
return (
|
||||||
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<FormControl isInvalid={Boolean(errors.email)} mb={'3'}>
|
<Field label="Email" invalid={Boolean(errors.email)} errorText={errors.email?.type === 'required' ? errors.email.message?.toString() : errors.email?.type === 'pattern' ? 'Invalid Email Address' : undefined} mb={'3'}>
|
||||||
<FormLabel htmlFor="email">Email</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
{...register('email', {
|
{...register('email', {
|
||||||
@@ -53,40 +51,33 @@ export default function LoginForm() {
|
|||||||
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<FormErrorMessage>
|
</Field>
|
||||||
{errors.email?.type === 'required' && errors.email.message?.toString()}
|
<Field label="Password" invalid={Boolean(errors.password)} errorText={errors.password?.message?.toString()} mb={'3'}>
|
||||||
{errors.email?.type === 'pattern' && 'Invalid Email Address'}
|
|
||||||
</FormErrorMessage>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl isInvalid={Boolean(errors.password)} mb={'3'}>
|
|
||||||
<FormLabel htmlFor="password">Password</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="password"
|
id="password"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
type="password"
|
type="password"
|
||||||
{...register('password', { required: 'You must specify a password' })}
|
{...register('password', { required: 'You must specify a password' })}
|
||||||
/>
|
/>
|
||||||
<FormErrorMessage>
|
</Field>
|
||||||
{errors.password && errors.password.message?.toString()}
|
|
||||||
</FormErrorMessage>
|
|
||||||
</FormControl>
|
|
||||||
<br/>
|
<br/>
|
||||||
<HStack>
|
<HStack>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
colorPalette="blue"
|
||||||
disabled={!isDirty}
|
disabled={!isDirty}
|
||||||
isLoading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Center mt={'5rem'}>
|
<Center mt={'5rem'}>
|
||||||
<Button as={Link} to={'/forgotpassword'}>Forgot Password</Button>
|
<Button asChild variant="outline"><Link to={'/forgotpassword'}>Forgot Password</Link></Button>
|
||||||
</Center>
|
</Center>
|
||||||
</form>
|
</form>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { IResetPassword } from '../../models/user'
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Container,
|
Container,
|
||||||
FormControl,
|
Input,
|
||||||
FormLabel,
|
|
||||||
Input, useToast, useColorModeValue, FormErrorMessage,
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {Field} from '../ui/field'
|
||||||
|
import {useColorModeValue} from '../ui/color-mode'
|
||||||
|
import {toaster} from '../ui/toaster'
|
||||||
import agent from '../../api/agent'
|
import agent from '../../api/agent'
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ interface Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface URL {
|
interface URL {
|
||||||
token: string
|
token?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ResetPassword() {
|
export default function ResetPassword() {
|
||||||
@@ -27,32 +28,29 @@ export default function ResetPassword() {
|
|||||||
watch,
|
watch,
|
||||||
formState: { errors, isSubmitting, isDirty },
|
formState: { errors, isSubmitting, isDirty },
|
||||||
} = useForm()
|
} = useForm()
|
||||||
const { token } = useParams<URL>()
|
const { token } = useParams()
|
||||||
const history = useNavigate()
|
const history = useNavigate()
|
||||||
const toast = useToast()
|
|
||||||
const password = useRef({})
|
const password = useRef({})
|
||||||
password.current = watch('password', '')
|
password.current = watch('password', '')
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async (data) => {
|
const onSubmit = handleSubmit(async (data) => {
|
||||||
const message = await agent.Account.reset(token, data as IResetPassword) as Message
|
const message = await agent.Account.reset(token!, data as IResetPassword) as Message
|
||||||
if (message.success) {
|
if (message.success) {
|
||||||
setTimeout(() => history('/login'), 3000)
|
setTimeout(() => history('/login'), 3000)
|
||||||
toast({
|
toaster.create({
|
||||||
title: 'Password Reset',
|
title: 'Password Reset',
|
||||||
status: 'success',
|
type: 'success',
|
||||||
description: 'Your password has been reset. You will be redirected to login automatically',
|
description: 'Your password has been reset. You will be redirected to login automatically',
|
||||||
isClosable: true,
|
closable: true,
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
position: 'top',
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toaster.create({
|
||||||
title: 'Password Not Reset',
|
title: 'Password Not Reset',
|
||||||
status: 'error',
|
type: 'error',
|
||||||
description: 'Something went wrong, please contact the website administrator',
|
description: 'Something went wrong, please contact the website administrator',
|
||||||
isClosable: true,
|
closable: true,
|
||||||
duration: 10000,
|
duration: 10000,
|
||||||
position: 'top',
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -62,15 +60,14 @@ export default function ResetPassword() {
|
|||||||
return (
|
return (
|
||||||
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<FormControl isInvalid={Boolean(errors.password)} mb={'3'}>
|
<Field label="Password" invalid={Boolean(errors.password)} errorText={errors.password?.message?.toString()} mb={'3'}>
|
||||||
<FormLabel htmlFor="password">Password</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="password"
|
id="password"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
|
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -82,18 +79,14 @@ export default function ResetPassword() {
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<FormErrorMessage>
|
</Field>
|
||||||
{errors.password && errors.password.message?.toString()}
|
<Field label="Repeat Password" invalid={Boolean(errors.repeat_password)} errorText={errors.repeat_password?.message?.toString()} mb={'3'}>
|
||||||
</FormErrorMessage>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl isInvalid={Boolean(errors.repeat_password)} mb={'3'}>
|
|
||||||
<FormLabel htmlFor="repeat_password">Repeat Password</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="repeat_password"
|
id="repeat_password"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder="Repeat Password"
|
placeholder="Repeat Password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -102,15 +95,13 @@ export default function ResetPassword() {
|
|||||||
value === password.current || 'The passwords do not match',
|
value === password.current || 'The passwords do not match',
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<FormErrorMessage>
|
</Field>
|
||||||
{errors.repeat_password && errors.repeat_password.message?.toString()}
|
|
||||||
</FormErrorMessage>
|
|
||||||
</FormControl>
|
|
||||||
<br/>
|
<br/>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
colorPalette="blue"
|
||||||
disabled={!isDirty}
|
disabled={!isDirty}
|
||||||
isLoading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -3,15 +3,13 @@ import { useForm } from 'react-hook-form'
|
|||||||
import { UserFormValues } from '../../models/user'
|
import { UserFormValues } from '../../models/user'
|
||||||
import { createUser } from '../../stateManagement/userState'
|
import { createUser } from '../../stateManagement/userState'
|
||||||
import {
|
import {
|
||||||
Alert,
|
|
||||||
AlertDescription,
|
|
||||||
AlertIcon,
|
|
||||||
Button,
|
Button,
|
||||||
Container,
|
Container,
|
||||||
FormControl, FormErrorMessage,
|
Input,
|
||||||
FormLabel,
|
|
||||||
Input, useColorModeValue,
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {Field} from '../ui/field'
|
||||||
|
import {Alert} from '../ui/alert'
|
||||||
|
import {useColorModeValue} from '../ui/color-mode'
|
||||||
|
|
||||||
export default function CreateUser() {
|
export default function CreateUser() {
|
||||||
const {
|
const {
|
||||||
@@ -44,59 +42,52 @@ export default function CreateUser() {
|
|||||||
return (
|
return (
|
||||||
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<FormControl isInvalid={Boolean(errors.name)} mb={'3'}>
|
<Field label="Full Name" invalid={Boolean(errors.name)} errorText={errors.name?.message?.toString()} mb={'3'}>
|
||||||
<FormLabel htmlFor='name'>Full Name</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id='name'
|
id='name'
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder='Full Name'
|
placeholder='Full Name'
|
||||||
{...register('name', { required: 'Name is required' })}
|
{...register('name', { required: 'Name is required' })}
|
||||||
/>
|
/>
|
||||||
{errors.name && <FormErrorMessage>{ errors.name.message?.toString()}</FormErrorMessage>}
|
</Field>
|
||||||
</FormControl>
|
<Field label="Display Name" invalid={Boolean(errors.displayName)} errorText={errors.displayName?.message?.toString()} mb={'3'}>
|
||||||
<FormControl isInvalid={Boolean(errors.displayName)} mb={'3'}>
|
|
||||||
<FormLabel htmlFor='displayName'>Display Name</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id='displayName'
|
id='displayName'
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
|
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder='Display Name'
|
placeholder='Display Name'
|
||||||
{...register('displayName', { required: 'Display Name is required' })}
|
{...register('displayName', { required: 'Display Name is required' })}
|
||||||
/>
|
/>
|
||||||
{errors.displayName && <FormErrorMessage>{ errors.displayName.message?.toString()}</FormErrorMessage>}
|
</Field>
|
||||||
</FormControl>
|
<Field label="Email" invalid={Boolean(errors.email)} errorText={errors.email?.message?.toString()} mb={'3'}>
|
||||||
<FormControl isInvalid={Boolean(errors.email)} mb={'3'}>
|
|
||||||
<FormLabel htmlFor='email'>Email</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id='email'
|
id='email'
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
|
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder='Email'
|
placeholder='Email'
|
||||||
{...register('email', { required: 'Email is required' })}
|
{...register('email', { required: 'Email is required' })}
|
||||||
/>
|
/>
|
||||||
{errors.email && <FormErrorMessage>{ errors.email.message?.toString()}</FormErrorMessage>}
|
</Field>
|
||||||
</FormControl>
|
<Field label="Password" invalid={Boolean(errors.password)} errorText={errors.password?.message?.toString()} mb={'3'}>
|
||||||
<FormControl isInvalid={Boolean(errors.password)} mb={'3'}>
|
|
||||||
<FormLabel htmlFor='password'>Password</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id='password'
|
id='password'
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
|
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder='Password'
|
placeholder='Password'
|
||||||
type='password'
|
type='password'
|
||||||
@@ -108,26 +99,22 @@ export default function CreateUser() {
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.password && <FormErrorMessage>{ errors.password.message?.toString()}</FormErrorMessage>}
|
</Field>
|
||||||
</FormControl>
|
|
||||||
{errors.password && (
|
{errors.password && (
|
||||||
<>
|
<>
|
||||||
<Alert status='error' style={{ marginBottom: 10 }}>
|
<Alert status='error' title={errors.password.message?.toString()} style={{ marginBottom: 10 }}>
|
||||||
<AlertIcon />
|
{errors.password.message?.toString()}
|
||||||
<AlertDescription>{errors.password.message?.toString()}</AlertDescription>
|
|
||||||
{/*<CloseButton position='absolute' right='8px' top='8px' />*/}
|
|
||||||
</Alert>
|
</Alert>
|
||||||
<br />
|
<br />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<FormControl isInvalid={Boolean(errors.repeat_password)} mb={'3'}>
|
<Field label="Repeat Password" invalid={Boolean(errors.repeat_password)} errorText={errors.repeat_password?.message?.toString()} mb={'3'}>
|
||||||
<FormLabel htmlFor='repeat_password'>Repeat Password</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id='repeat_password'
|
id='repeat_password'
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder='Repeat Password'
|
placeholder='Repeat Password'
|
||||||
type='password'
|
type='password'
|
||||||
@@ -136,22 +123,20 @@ export default function CreateUser() {
|
|||||||
value === password.current || 'The passwords do not match',
|
value === password.current || 'The passwords do not match',
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.repeat_password && <FormErrorMessage>{ errors.repeat_password.message?.toString()}</FormErrorMessage>}
|
</Field>
|
||||||
</FormControl>
|
|
||||||
{errors.repeat_password && (
|
{errors.repeat_password && (
|
||||||
<>
|
<>
|
||||||
<Alert status='error' style={{ marginBottom: 10 }}>
|
<Alert status='error' title={errors.repeat_password.message?.toString()} style={{ marginBottom: 10 }}>
|
||||||
<AlertIcon />
|
{errors.repeat_password.message?.toString()}
|
||||||
<AlertDescription>{errors.repeat_password.message?.toString()}</AlertDescription>
|
|
||||||
{/*<CloseButton position='absolute' right='8px' top='8px' />*/}
|
|
||||||
</Alert>
|
</Alert>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<Button
|
<Button
|
||||||
type='submit'
|
type='submit'
|
||||||
|
colorPalette="blue"
|
||||||
disabled={!isDirty}
|
disabled={!isDirty}
|
||||||
isLoading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import { loadedPreferences, loggedInUser, updateUserPreferences } from '../../st
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Container, Flex,
|
Container, Flex,
|
||||||
FormControl,
|
|
||||||
FormLabel,
|
|
||||||
Heading,
|
Heading,
|
||||||
Input,
|
Input,
|
||||||
Stack, Switch,
|
Stack,
|
||||||
useColorModeValue,
|
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {Field} from '../ui/field'
|
||||||
|
import {Switch} from '../ui/switch'
|
||||||
|
import {useColorModeValue} from '../ui/color-mode'
|
||||||
|
import {toaster} from '../ui/toaster'
|
||||||
import { useHookstate } from '@hookstate/core'
|
import { useHookstate } from '@hookstate/core'
|
||||||
import agent from '../../api/agent'
|
import agent from '../../api/agent'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
@@ -43,12 +43,10 @@ export default function EditUser() {
|
|||||||
const history = useNavigate()
|
const history = useNavigate()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reset(user)
|
reset(user as any)
|
||||||
return
|
return
|
||||||
}, [user.name !== ''])
|
}, [user.name !== ''])
|
||||||
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
success: boolean
|
success: boolean
|
||||||
data: string
|
data: string
|
||||||
@@ -66,13 +64,12 @@ export default function EditUser() {
|
|||||||
|
|
||||||
const engageChangePassword = async (values: IForgotPassword) => {
|
const engageChangePassword = async (values: IForgotPassword) => {
|
||||||
const message = await agent.Account.forgot(values as IForgotPassword) as Message
|
const message = await agent.Account.forgot(values as IForgotPassword) as Message
|
||||||
toast({
|
toaster.create({
|
||||||
title: 'Email Sent',
|
title: 'Email Sent',
|
||||||
status: 'success',
|
type: 'success',
|
||||||
description: message.data,
|
description: message.data,
|
||||||
isClosable: true,
|
closable: true,
|
||||||
duration: 10000,
|
duration: 10000,
|
||||||
position: 'top'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,78 +87,70 @@ export default function EditUser() {
|
|||||||
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
<Container style={{ marginTop: '7rem', paddingBottom: '5rem' }}>
|
||||||
<Heading as="h1" marginBottom={'3rem'}>Edit {user.displayName}</Heading>
|
<Heading as="h1" marginBottom={'3rem'}>Edit {user.displayName}</Heading>
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<FormControl>
|
<Field label="Full Name">
|
||||||
<FormLabel htmlFor="name">Full Name</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="name"
|
id="name"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
{...register('name', { required: 'Name is required' })}
|
{...register('name', { required: 'Name is required' })}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</Field>
|
||||||
<FormControl mt={'3'}>
|
<Field label="Display Name" mt={'3'}>
|
||||||
<FormLabel htmlFor="displayName">Display Name</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="displayName"
|
id="displayName"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
{...register('displayName', { required: 'Display Name is required' })}
|
{...register('displayName', { required: 'Display Name is required' })}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</Field>
|
||||||
<FormControl mt={'3'}>
|
<Field label="Email" mt={'3'}>
|
||||||
<FormLabel htmlFor="email">Email</FormLabel>
|
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_placeholder={{ color: 'gray.500' }}
|
_placeholder={{ color: 'gray.500' }}
|
||||||
isDisabled
|
disabled
|
||||||
border="1px"
|
borderWidth="1px" borderStyle="solid"
|
||||||
borderColor="gray.500"
|
borderColor={{ base: "gray.500", _invalid: "red.500" }}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
{...register('email', { required: 'Email is required' })}
|
{...register('email', { required: 'Email is required' })}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</Field>
|
||||||
<FormControl>
|
<Field label="Dark Mode:" orientation="horizontal" mt={'6'}>
|
||||||
<Flex mt={'6'}>
|
|
||||||
<FormLabel htmlFor={"theme"}>Dark Mode:</FormLabel>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="theme"
|
name="theme"
|
||||||
render={({ field: { onChange, value, ref } }) => (
|
render={({ field: { onChange, value, ref } }) => (
|
||||||
<Switch isChecked={value} onChange={onChange} ref={ref} />
|
<Switch colorPalette="blue" checked={value} onCheckedChange={({ checked }: any) => onChange(checked)} ref={ref} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Field>
|
||||||
</FormControl>
|
<Field label="Sticky Navbar:" orientation="horizontal" mt={'6'}>
|
||||||
<FormControl>
|
|
||||||
<Flex mt={'6'}>
|
|
||||||
<FormLabel htmlFor={"stickyNav"}>Sticky Navbar:</FormLabel>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="stickyNav"
|
name="stickyNav"
|
||||||
render={({ field: { onChange, value, ref } }) => (
|
render={({ field: { onChange, value, ref } }) => (
|
||||||
<Switch isChecked={value} onChange={onChange} ref={ref} />
|
<Switch colorPalette="blue" checked={value} onCheckedChange={({ checked }: any) => onChange(checked)} ref={ref} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Field>
|
||||||
</FormControl>
|
|
||||||
<br />
|
<br />
|
||||||
<Stack spacing={[1, 5]} direction={'row'} align="self-end" pb="3">
|
<Stack gap={[1, 5]} direction={'row'} alignSelf="flex-end" pb="3">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
colorPalette="blue"
|
||||||
disabled={!isDirty}
|
disabled={!isDirty}
|
||||||
isLoading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isLoading={passwordLoading.get()}
|
loading={passwordLoading.get()}
|
||||||
isDisabled={passwordDisabled.get()}
|
disabled={passwordDisabled.get()}
|
||||||
onMouseDown={async () => {
|
onMouseDown={async () => {
|
||||||
passwordLoading.set(true)
|
passwordLoading.set(true)
|
||||||
passwordDisabled.set(true)
|
passwordDisabled.set(true)
|
||||||
|
|||||||
@@ -7,14 +7,10 @@ import {
|
|||||||
GridItem,
|
GridItem,
|
||||||
Heading,
|
Heading,
|
||||||
Image,
|
Image,
|
||||||
Table,
|
|
||||||
TableContainer,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Text,
|
Text,
|
||||||
Tr,
|
Table,
|
||||||
useColorModeValue
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
|
import {useColorModeValue} from '../ui/color-mode'
|
||||||
import {useHookstate} from '@hookstate/core'
|
import {useHookstate} from '@hookstate/core'
|
||||||
import {loggedInUser} from '../../stateManagement/userState'
|
import {loggedInUser} from '../../stateManagement/userState'
|
||||||
import {Link} from 'react-router-dom'
|
import {Link} from 'react-router-dom'
|
||||||
@@ -28,10 +24,12 @@ const UserProfile = () => {
|
|||||||
<Heading mb={'1rem'} as="h1">User Profile</Heading>
|
<Heading mb={'1rem'} as="h1">User Profile</Heading>
|
||||||
<Grid templateColumns={`repeat(7, 1fr)`} templateRows={'2'} gap={6}>
|
<Grid templateColumns={`repeat(7, 1fr)`} templateRows={'2'} gap={6}>
|
||||||
<GridItem rowStart={1} rowEnd={1} colStart={7} alignSelf={'end'} justifySelf={'end'}>
|
<GridItem rowStart={1} rowEnd={1} colStart={7} alignSelf={'end'} justifySelf={'end'}>
|
||||||
<Button as={Link} to={`/edit-user`} colorScheme="blue" style={{textAlign: 'right'}}>
|
<Button asChild colorPalette="blue" style={{textAlign: 'right'}}>
|
||||||
<Text mt={'1'}>
|
<Link to={`/edit-user`}>
|
||||||
Edit
|
<Text mt={'1'}>
|
||||||
</Text>
|
Edit
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem rowStart={2} rowEnd={2} colSpan={2} colStart={2}>
|
<GridItem rowStart={2} rowEnd={2} colSpan={2} colStart={2}>
|
||||||
@@ -42,28 +40,28 @@ const UserProfile = () => {
|
|||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem rowStart={2} rowEnd={2} colStart={4} colEnd={8}>
|
<GridItem rowStart={2} rowEnd={2} colStart={4} colEnd={8}>
|
||||||
<Box bg={bg} p="5" rounded="md">
|
<Box bg={bg} p="5" rounded="md">
|
||||||
<TableContainer>
|
<Table.ScrollArea>
|
||||||
<Table variant="simple">
|
<Table.Root variant="outline">
|
||||||
<Tbody>
|
<Table.Body>
|
||||||
<Tr>
|
<Table.Row>
|
||||||
<Td><Text fontSize={'1.5rem'}>Display Name: </Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>Display Name: </Text></Table.Cell>
|
||||||
<Td><Text fontSize={'1.5rem'}>{user.displayName}</Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>{user.displayName}</Text></Table.Cell>
|
||||||
</Tr>
|
</Table.Row>
|
||||||
<Tr>
|
<Table.Row>
|
||||||
<Td><Text fontSize={'1.5rem'}>Full Name: </Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>Full Name: </Text></Table.Cell>
|
||||||
<Td><Text fontSize={'1.5rem'}>{user.name}</Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>{user.name}</Text></Table.Cell>
|
||||||
</Tr>
|
</Table.Row>
|
||||||
<Tr>
|
<Table.Row>
|
||||||
<Td><Text fontSize={'1.5rem'}>Email: </Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>Email: </Text></Table.Cell>
|
||||||
<Td><Text fontSize={'1.5rem'}>{user.email}</Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>{user.email}</Text></Table.Cell>
|
||||||
</Tr>
|
</Table.Row>
|
||||||
<Tr>
|
<Table.Row>
|
||||||
<Td><Text fontSize={'1.5rem'}>Role: </Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>Role: </Text></Table.Cell>
|
||||||
<Td><Text fontSize={'1.5rem'}>{user.role}</Text></Td>
|
<Table.Cell><Text fontSize={'1.5rem'}>{user.role}</Text></Table.Cell>
|
||||||
</Tr>
|
</Table.Row>
|
||||||
</Tbody>
|
</Table.Body>
|
||||||
</Table>
|
</Table.Root>
|
||||||
</TableContainer>
|
</Table.ScrollArea>
|
||||||
</Box>
|
</Box>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export default function NotFound() {
|
|||||||
It looks like you hit a{' '}
|
It looks like you hit a{' '}
|
||||||
{<span style={styles.retroFontBody}>DEAD</span>} end!
|
{<span style={styles.retroFontBody}>DEAD</span>} end!
|
||||||
</Heading>
|
</Heading>
|
||||||
<Button colorScheme='blue' as={Link} to={'/games'} style={{fontSize: '1.2rem', marginLeft: '5rem'}}>
|
<Button colorPalette='blue' asChild style={{fontSize: '1.2rem', marginLeft: '5rem'}}>
|
||||||
Back to Games List
|
<Link to={'/games'}>Back to Games List</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Suspense fallback={<LoadingModal />}>
|
<Suspense fallback={<LoadingModal />}>
|
||||||
<Video/>
|
<Video/>
|
||||||
|
|||||||
@@ -29,10 +29,12 @@ export default function SomethingWentWrong() {
|
|||||||
{<span style={styles.retroFontBody}>WE APOLOGIZE!</span>}<br/>
|
{<span style={styles.retroFontBody}>WE APOLOGIZE!</span>}<br/>
|
||||||
Please contact the website administrator!
|
Please contact the website administrator!
|
||||||
</Heading>
|
</Heading>
|
||||||
<Button colorScheme='blue' as={Link} to={'/games'} style={{fontSize: '1.2rem', marginLeft: '5rem'}}>
|
<Button colorPalette='blue' asChild style={{fontSize: '1.2rem', marginLeft: '5rem'}}>
|
||||||
|
<Link to={'/games'}>
|
||||||
<div style={{marginTop: '.25rem'}}>
|
<div style={{marginTop: '.25rem'}}>
|
||||||
Back to Games List
|
Back to Games List
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Suspense fallback={<LoadingModal/>}>
|
<Suspense fallback={<LoadingModal/>}>
|
||||||
<Video/>
|
<Video/>
|
||||||
|
|||||||
Reference in New Issue
Block a user