all
This commit is contained in:
@ -0,0 +1,171 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from '@chakra-ui/react'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import {
|
||||
Field,
|
||||
FieldAttributes,
|
||||
FieldProps,
|
||||
Form,
|
||||
Formik,
|
||||
FormikHelpers,
|
||||
} from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import cx from 'classnames'
|
||||
import UserAPI, { User } from '@/client/idp/user'
|
||||
|
||||
export type AccountChangePasswordProps = {
|
||||
open: boolean
|
||||
user: User
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
type FormValues = {
|
||||
currentPassword: string
|
||||
newPassword: string
|
||||
}
|
||||
|
||||
const AccountChangePassword = ({
|
||||
open,
|
||||
onClose,
|
||||
}: AccountChangePasswordProps) => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const initialValues: FormValues = { currentPassword: '', newPassword: '' }
|
||||
const formSchema = Yup.object().shape({
|
||||
currentPassword: Yup.string().required('Current password is required'),
|
||||
newPassword: Yup.string().required('New password is required'),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setIsModalOpen(open)
|
||||
}, [open])
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
{ currentPassword, newPassword }: FormValues,
|
||||
{ setSubmitting }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const result = await UserAPI.updatePassword({
|
||||
currentPassword,
|
||||
newPassword,
|
||||
})
|
||||
mutate(`/user`, result)
|
||||
setSubmitting(false)
|
||||
onClose?.()
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[onClose, mutate],
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => onClose?.()}
|
||||
closeOnOverlayClick={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Change Password</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ errors, touched, isSubmitting }) => (
|
||||
<Form>
|
||||
<ModalBody>
|
||||
<div className={cx('flex', 'flex-col', 'gap-1.5')}>
|
||||
<Field name="currentPassword">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
isInvalid={
|
||||
errors.currentPassword && touched.currentPassword
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="Current password"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
<FormErrorMessage>
|
||||
{errors.currentPassword}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="newPassword">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
isInvalid={
|
||||
errors.newPassword && touched.newPassword
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="New password"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
<FormErrorMessage>
|
||||
{errors.newPassword}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => onClose?.()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountChangePassword
|
@ -0,0 +1,142 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from '@chakra-ui/react'
|
||||
import {
|
||||
Field,
|
||||
FieldAttributes,
|
||||
FieldProps,
|
||||
Form,
|
||||
Formik,
|
||||
FormikHelpers,
|
||||
} from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import cx from 'classnames'
|
||||
import UserAPI from '@/client/idp/user'
|
||||
|
||||
export type AccountDeleteProps = {
|
||||
open: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
type FormValues = {
|
||||
password: string
|
||||
}
|
||||
|
||||
const AccountDelete = ({ open, onClose }: AccountDeleteProps) => {
|
||||
const navigate = useNavigate()
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const formSchema = Yup.object().shape({
|
||||
password: Yup.string().required('Password is required'),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setIsModalOpen(open)
|
||||
}, [open])
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
{ password }: FormValues,
|
||||
{ setSubmitting }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
setSubmitting(true)
|
||||
try {
|
||||
await UserAPI.delete({ password })
|
||||
navigate('/sign-in')
|
||||
onClose?.()
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[navigate, onClose],
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => onClose?.()}
|
||||
closeOnOverlayClick={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Delete Account</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Formik
|
||||
initialValues={{ password: '' }}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ errors, touched, isSubmitting }) => (
|
||||
<Form>
|
||||
<ModalBody>
|
||||
<div className={cx('flex', 'flex-col', 'items-start', 'gap-1')}>
|
||||
<span>
|
||||
Are you sure you would like to delete your account
|
||||
permanently?
|
||||
</span>
|
||||
<span className={cx('font-semibold')}>
|
||||
Type your password to confirm:
|
||||
</span>
|
||||
<Field name="password">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
isInvalid={
|
||||
errors.password && touched.password ? true : false
|
||||
}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
<FormErrorMessage>{errors.password}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => onClose?.()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="solid"
|
||||
colorScheme="red"
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Delete Permanently
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountDelete
|
@ -0,0 +1,139 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from '@chakra-ui/react'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import {
|
||||
Field,
|
||||
FieldAttributes,
|
||||
FieldProps,
|
||||
Form,
|
||||
Formik,
|
||||
FormikHelpers,
|
||||
} from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import cx from 'classnames'
|
||||
import UserAPI, { User } from '@/client/idp/user'
|
||||
|
||||
export type AccountEditEmailProps = {
|
||||
open: boolean
|
||||
user: User
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
type FormValues = {
|
||||
email: string
|
||||
}
|
||||
|
||||
const AccountEditEmail = ({ open, user, onClose }: AccountEditEmailProps) => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const formSchema = Yup.object().shape({
|
||||
email: Yup.string()
|
||||
.required('Email is required')
|
||||
.email('Must be a valid email')
|
||||
.max(255),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setIsModalOpen(open)
|
||||
}, [open])
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
{ email }: FormValues,
|
||||
{ setSubmitting }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const result = await UserAPI.updateEmailRequest({
|
||||
email,
|
||||
})
|
||||
mutate(`/user`, result)
|
||||
setSubmitting(false)
|
||||
onClose?.()
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[onClose, mutate],
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => onClose?.()}
|
||||
closeOnOverlayClick={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Edit Email</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Formik
|
||||
enableReinitialize={true}
|
||||
initialValues={{ email: user?.pendingEmail || user?.email }}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ errors, touched, isSubmitting }) => (
|
||||
<Form>
|
||||
<ModalBody>
|
||||
<Field name="email">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
isInvalid={errors.email && touched.email ? true : false}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Email"
|
||||
disabled={isSubmitting}
|
||||
autoFocus
|
||||
/>
|
||||
<FormErrorMessage>{errors.email}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => onClose?.()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountEditEmail
|
@ -0,0 +1,142 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from '@chakra-ui/react'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import {
|
||||
Field,
|
||||
FieldAttributes,
|
||||
FieldProps,
|
||||
Form,
|
||||
Formik,
|
||||
FormikHelpers,
|
||||
} from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import cx from 'classnames'
|
||||
import UserAPI, { User } from '@/client/idp/user'
|
||||
|
||||
export type AccountEditFullNameProps = {
|
||||
open: boolean
|
||||
user: User
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
type FormValues = {
|
||||
fullName: string
|
||||
}
|
||||
|
||||
const AccountEditFullName = ({
|
||||
open,
|
||||
user,
|
||||
onClose,
|
||||
}: AccountEditFullNameProps) => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const formSchema = Yup.object().shape({
|
||||
fullName: Yup.string().required('Full name is required').max(255),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setIsModalOpen(open)
|
||||
}, [open])
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
{ fullName }: FormValues,
|
||||
{ setSubmitting }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const result = await UserAPI.updateFullName({
|
||||
fullName,
|
||||
})
|
||||
mutate(`/user`, result)
|
||||
setSubmitting(false)
|
||||
onClose?.()
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[onClose, mutate],
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => onClose?.()}
|
||||
closeOnOverlayClick={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Edit Full Name</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Formik
|
||||
enableReinitialize={true}
|
||||
initialValues={{ fullName: user.fullName }}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ errors, touched, isSubmitting }) => (
|
||||
<Form>
|
||||
<ModalBody>
|
||||
<Field name="fullName">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
isInvalid={
|
||||
errors.fullName && touched.fullName ? true : false
|
||||
}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Full name"
|
||||
disabled={isSubmitting}
|
||||
autoFocus
|
||||
/>
|
||||
<FormErrorMessage>{errors.fullName}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => onClose?.()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountEditFullName
|
@ -0,0 +1,121 @@
|
||||
import { ChangeEvent, useCallback, useRef, useState } from 'react'
|
||||
import { IconButton, Image } from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { IconEdit } from '@/lib'
|
||||
|
||||
export type EditPictureUploadProps = {
|
||||
name: string
|
||||
initialValue?: string
|
||||
disabled: boolean
|
||||
onChange: (event: any) => void
|
||||
}
|
||||
|
||||
const EditPictureUpload = ({
|
||||
name,
|
||||
initialValue,
|
||||
onChange,
|
||||
disabled,
|
||||
}: EditPictureUploadProps) => {
|
||||
const [src, setSrc] = useState<string>()
|
||||
const hiddenInput = useRef<HTMLInputElement>(null)
|
||||
|
||||
const handleFileChange = useCallback(
|
||||
(changeEvent: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!changeEvent.target.files || changeEvent.target.files.length === 0) {
|
||||
return
|
||||
}
|
||||
const file = changeEvent.target.files.item(0)
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.onload = (readerEvent: ProgressEvent<FileReader>) => {
|
||||
if (
|
||||
readerEvent.target?.result &&
|
||||
typeof readerEvent.target.result === 'string'
|
||||
) {
|
||||
setSrc(readerEvent.target.result)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
onChange?.(changeEvent)
|
||||
},
|
||||
[onChange],
|
||||
)
|
||||
|
||||
const handleEdit = useCallback(() => {
|
||||
if (!disabled && hiddenInput.current) {
|
||||
hiddenInput.current.click()
|
||||
}
|
||||
}, [disabled, hiddenInput])
|
||||
|
||||
return (
|
||||
<div className={cx('flex', 'flex-col', 'items-center', 'gap-1')}>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'rounded',
|
||||
'border',
|
||||
'border-dashed',
|
||||
'border-blue-600',
|
||||
'dark:border-blue-200',
|
||||
)}
|
||||
>
|
||||
{src || initialValue ? (
|
||||
<div className={cx('relative', 'w-[400px]', 'h-[160px]')}>
|
||||
<Image
|
||||
src={src || initialValue || ''}
|
||||
className={cx(
|
||||
'rounded',
|
||||
'w-[400px]',
|
||||
'h-[160px]',
|
||||
'object-cover',
|
||||
)}
|
||||
alt="Account picture"
|
||||
/>
|
||||
<IconButton
|
||||
icon={<IconEdit />}
|
||||
variant="solid-gray"
|
||||
className={cx(
|
||||
'top-[10px]',
|
||||
'right-[5px]',
|
||||
'absolute',
|
||||
'z-[1000]',
|
||||
)}
|
||||
aria-label=""
|
||||
disabled={disabled}
|
||||
onClick={handleEdit}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'cursor-pointer',
|
||||
'w-[400px]',
|
||||
'h-[160px]',
|
||||
)}
|
||||
onClick={handleEdit}
|
||||
>
|
||||
<span className={cx('text-blue-600', 'dark:text-blue-200')}>
|
||||
Browse
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
ref={hiddenInput}
|
||||
className={cx('hidden')}
|
||||
type="file"
|
||||
name={name}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditPictureUpload
|
@ -0,0 +1,186 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from '@chakra-ui/react'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import {
|
||||
Field,
|
||||
FieldAttributes,
|
||||
FieldProps,
|
||||
Form,
|
||||
Formik,
|
||||
FormikHelpers,
|
||||
} from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import cx from 'classnames'
|
||||
import UserAPI, { User } from '@/client/idp/user'
|
||||
import EditPictureUpload from './edit-picture-upload'
|
||||
|
||||
export type AccountEditPictureProps = {
|
||||
open: boolean
|
||||
user: User
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
type FormValues = {
|
||||
picture: any
|
||||
}
|
||||
|
||||
const AccountEditPicture = ({
|
||||
open,
|
||||
user,
|
||||
onClose,
|
||||
}: AccountEditPictureProps) => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const [deletionInProgress, setDeletionInProgress] = useState(false)
|
||||
const formSchema = Yup.object().shape({
|
||||
picture: Yup.mixed()
|
||||
.required()
|
||||
.test(
|
||||
'fileSize',
|
||||
'Image is too big, should be less than 3 MB',
|
||||
(value: any) => value === null || (value && value.size <= 3000000),
|
||||
)
|
||||
.test(
|
||||
'fileType',
|
||||
'Unsupported file format',
|
||||
(value: any) =>
|
||||
value === null ||
|
||||
(value &&
|
||||
['image/jpg', 'image/jpeg', 'image/gif', 'image/png'].includes(
|
||||
value.type,
|
||||
)),
|
||||
),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setIsModalOpen(open)
|
||||
}, [open])
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
{ picture }: FormValues,
|
||||
{ setSubmitting }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
setSubmitting(true)
|
||||
try {
|
||||
const result = await UserAPI.updatePicture(picture)
|
||||
mutate(`/user`, result)
|
||||
setSubmitting(false)
|
||||
onClose?.()
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[onClose, mutate],
|
||||
)
|
||||
|
||||
const handleDelete = useCallback(async () => {
|
||||
try {
|
||||
setDeletionInProgress(true)
|
||||
const result = await UserAPI.deletePicture()
|
||||
mutate(`/user`, result)
|
||||
onClose?.()
|
||||
} finally {
|
||||
setDeletionInProgress(false)
|
||||
}
|
||||
}, [onClose, mutate])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => onClose?.()}
|
||||
closeOnOverlayClick={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Edit Picture</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Formik
|
||||
enableReinitialize={true}
|
||||
initialValues={{
|
||||
picture: user.picture,
|
||||
}}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ errors, touched, isSubmitting, setFieldValue, values }) => (
|
||||
<Form>
|
||||
<ModalBody>
|
||||
<div
|
||||
className={cx('flex', 'flex-col', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Field name="picture">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
isInvalid={
|
||||
errors.picture && touched.picture ? true : false
|
||||
}
|
||||
>
|
||||
<EditPictureUpload
|
||||
{...field}
|
||||
initialValue={user.picture}
|
||||
disabled={isSubmitting}
|
||||
onChange={(e) =>
|
||||
setFieldValue('picture', e.target.files[0])
|
||||
}
|
||||
/>
|
||||
<FormErrorMessage>{errors.picture}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => onClose?.()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
colorScheme="red"
|
||||
isLoading={deletionInProgress}
|
||||
disabled={!user.picture}
|
||||
onClick={handleDelete}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
disabled={isSubmitting || values.picture === user.picture}
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountEditPicture
|
Reference in New Issue
Block a user