From 35c306b5557237a89a97134bf335d4d8db493cd2 Mon Sep 17 00:00:00 2001 From: Alejandra <90076947+alejsdev@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:30:59 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20form=20validation=20to=20Admi?= =?UTF-8?q?n,=20Items=20and=20Login=20(#616)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Admin/AddUser.tsx | 63 +++++++++++------- .../src/components/Admin/EditUser.tsx | 66 +++++++++++-------- .../src/components/Common/DeleteAlert.tsx | 10 +-- .../src/components/Items/AddItem.tsx | 26 ++++---- .../src/components/Items/EditItem.tsx | 7 +- .../components/UserSettings/Appearance.tsx | 4 +- .../UserSettings/DeleteConfirmation.tsx | 4 +- .../UserSettings/UserInformation.tsx | 2 +- src/new-frontend/src/pages/Login.tsx | 48 +++++++++----- 9 files changed, 136 insertions(+), 94 deletions(-) diff --git a/src/new-frontend/src/components/Admin/AddUser.tsx b/src/new-frontend/src/components/Admin/AddUser.tsx index 315d6ddfe6e..4a99aa32782 100644 --- a/src/new-frontend/src/components/Admin/AddUser.tsx +++ b/src/new-frontend/src/components/Admin/AddUser.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Button, Checkbox, Flex, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; +import { Button, Checkbox, Flex, FormControl, FormErrorMessage, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; import { SubmitHandler, useForm } from 'react-hook-form'; import { UserCreate } from '../../client'; @@ -14,29 +14,35 @@ interface AddUserProps { } interface UserCreateForm extends UserCreate { - confirmPassword: string; + confirm_password: string; } const AddUser: React.FC = ({ isOpen, onClose }) => { const showToast = useCustomToast(); - const { register, handleSubmit, reset, formState: { isSubmitting } } = useForm(); + const { register, handleSubmit, reset, getValues, formState: { errors, isSubmitting } } = useForm({ + mode: 'onBlur', + criteriaMode: 'all', + defaultValues: { + email: '', + full_name: '', + password: '', + confirm_password: '', + is_superuser: false, + is_active: false + } + }); const { addUser } = useUsersStore(); const onSubmit: SubmitHandler = async (data) => { - if (data.password === data.confirmPassword) { - try { - await addUser(data); - showToast('Success!', 'User created successfully.', 'success'); - reset(); - onClose(); - } catch (err) { - const errDetail = (err as ApiError).body.detail; - showToast('Something went wrong.', `${errDetail}`, 'error'); - } - } else { - // TODO: Complete when form validation is implemented - console.log("Passwords don't match") + try { + await addUser(data); + showToast('Success!', 'User created successfully.', 'success'); + reset(); + onClose(); + } catch (err) { + const errDetail = (err as ApiError).body.detail; + showToast('Something went wrong.', `${errDetail}`, 'error'); } } @@ -53,21 +59,28 @@ const AddUser: React.FC = ({ isOpen, onClose }) => { Add User - + Email - + + {errors.email && {errors.email.message}} - + Full name + {errors.full_name && {errors.full_name.message}} - + Set Password - + + {errors.password && {errors.password.message}} - - Confirm Password - + + Confirm Password + value === getValues().password || 'The passwords do not match' + })} placeholder='Password' type='password' /> + {errors.confirm_password && {errors.confirm_password.message}} @@ -79,7 +92,7 @@ const AddUser: React.FC = ({ isOpen, onClose }) => { - diff --git a/src/new-frontend/src/components/Admin/EditUser.tsx b/src/new-frontend/src/components/Admin/EditUser.tsx index 465a856c559..06a818f8039 100644 --- a/src/new-frontend/src/components/Admin/EditUser.tsx +++ b/src/new-frontend/src/components/Admin/EditUser.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Button, Checkbox, Flex, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; +import { Button, Checkbox, Flex, FormControl, FormErrorMessage, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; import { SubmitHandler, useForm } from 'react-hook-form'; import { ApiError, UserUpdate } from '../../client'; @@ -19,25 +19,34 @@ interface UserUpdateForm extends UserUpdate { const EditUser: React.FC = ({ user_id, isOpen, onClose }) => { const showToast = useCustomToast(); - const { register, handleSubmit, reset, formState: { isSubmitting } } = useForm(); const { editUser, users } = useUsersStore(); - const currentUser = users.find((user) => user.id === user_id); + const { register, handleSubmit, reset, getValues, formState: { errors, isSubmitting } } = useForm({ + mode: 'onBlur', + criteriaMode: 'all', + defaultValues: { + email: currentUser?.email, + full_name: currentUser?.full_name, + password: '', + confirm_password: '', + is_superuser: currentUser?.is_superuser, + is_active: currentUser?.is_active + } + }); + const onSubmit: SubmitHandler = async (data) => { - if (data.password === data.confirm_password) { - try { - await editUser(user_id, data); - showToast('Success!', 'User updated successfully.', 'success'); - reset(); - onClose(); - } catch (err) { - const errDetail = (err as ApiError).body.detail; - showToast('Something went wrong.', `${errDetail}`, 'error'); + try { + if (data.password === '') { + delete data.password; } - } else { - // TODO: Complete when form validation is implemented - console.log("Passwords don't match") + await editUser(user_id, data); + showToast('Success!', 'User updated successfully.', 'success'); + reset(); + onClose(); + } catch (err) { + const errDetail = (err as ApiError).body.detail; + showToast('Something went wrong.', `${errDetail}`, 'error'); } } @@ -59,28 +68,33 @@ const EditUser: React.FC = ({ user_id, isOpen, onClose }) => { Edit User - + Email - + + {errors.email && {errors.email.message}} Full name - + - - Password - + + Set Password + + {errors.password && {errors.password.message}} - - Confirmation Password - + + Confirm Password + value === getValues().password || 'The passwords do not match' + })} placeholder='••••••••' type='password' /> + {errors.confirm_password && {errors.confirm_password.message}} - Is superuser? + Is superuser? - Is active? + Is active? diff --git a/src/new-frontend/src/components/Common/DeleteAlert.tsx b/src/new-frontend/src/components/Common/DeleteAlert.tsx index 68ec81c7b21..f6c70cdebf7 100644 --- a/src/new-frontend/src/components/Common/DeleteAlert.tsx +++ b/src/new-frontend/src/components/Common/DeleteAlert.tsx @@ -17,21 +17,17 @@ interface DeleteProps { const Delete: React.FC = ({ type, id, isOpen, onClose }) => { const showToast = useCustomToast(); const cancelRef = React.useRef(null); - const [isLoading, setIsLoading] = useState(false); - const { handleSubmit } = useForm(); + const { handleSubmit, formState: {isSubmitting} } = useForm(); const { deleteItem } = useItemsStore(); const { deleteUser } = useUsersStore(); const onSubmit = async () => { - setIsLoading(true); try { type === 'Item' ? await deleteItem(id) : await deleteUser(id); showToast('Success', `The ${type.toLowerCase()} was deleted successfully.`, 'success'); onClose(); } catch (err) { showToast('An error occurred.', `An error occurred while deleting the ${type.toLowerCase()}.`, 'error'); - } finally { - setIsLoading(false); } } @@ -56,10 +52,10 @@ const Delete: React.FC = ({ type, id, isOpen, onClose }) => { - - diff --git a/src/new-frontend/src/components/Items/AddItem.tsx b/src/new-frontend/src/components/Items/AddItem.tsx index 99fc621c22b..3c62a87c941 100644 --- a/src/new-frontend/src/components/Items/AddItem.tsx +++ b/src/new-frontend/src/components/Items/AddItem.tsx @@ -1,6 +1,6 @@ -import React, { useState } from 'react'; +import React from 'react'; -import { Button, FormControl, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; +import { Button, FormControl, FormErrorMessage, FormLabel, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay } from '@chakra-ui/react'; import { SubmitHandler, useForm } from 'react-hook-form'; import { ApiError, ItemCreate } from '../../client'; @@ -14,12 +14,17 @@ interface AddItemProps { const AddItem: React.FC = ({ isOpen, onClose }) => { const showToast = useCustomToast(); - const [isLoading, setIsLoading] = useState(false); - const { register, handleSubmit, reset } = useForm(); + const { register, handleSubmit, reset, formState: { errors, isSubmitting } } = useForm({ + mode: 'onBlur', + criteriaMode: 'all', + defaultValues: { + title: '', + description: '', + }, + }); const { addItem } = useItemsStore(); const onSubmit: SubmitHandler = async (data) => { - setIsLoading(true); try { await addItem(data); showToast('Success!', 'Item created successfully.', 'success'); @@ -28,8 +33,6 @@ const AddItem: React.FC = ({ isOpen, onClose }) => { } catch (err) { const errDetail = (err as ApiError).body.detail; showToast('Something went wrong.', `${errDetail}`, 'error'); - } finally { - setIsLoading(false); } }; @@ -46,14 +49,15 @@ const AddItem: React.FC = ({ isOpen, onClose }) => { Add Item - + Title + {errors.title && {errors.title.message}} Description @@ -67,10 +71,10 @@ const AddItem: React.FC = ({ isOpen, onClose }) => { - - diff --git a/src/new-frontend/src/components/Items/EditItem.tsx b/src/new-frontend/src/components/Items/EditItem.tsx index ce2d84beb46..d72d3ef7802 100644 --- a/src/new-frontend/src/components/Items/EditItem.tsx +++ b/src/new-frontend/src/components/Items/EditItem.tsx @@ -15,10 +15,9 @@ interface EditItemProps { const EditItem: React.FC = ({ id, isOpen, onClose }) => { const showToast = useCustomToast(); - const { register, handleSubmit, reset, formState: { isSubmitting }, } = useForm(); const { editItem, items } = useItemsStore(); - const currentItem = items.find((item) => item.id === id); + const { register, handleSubmit, reset, formState: { isSubmitting }, } = useForm({ defaultValues: { title: currentItem?.title, description: currentItem?.description } }); const onSubmit: SubmitHandler = async (data) => { try { @@ -52,11 +51,11 @@ const EditItem: React.FC = ({ id, isOpen, onClose }) => { Title - + Description - + diff --git a/src/new-frontend/src/components/UserSettings/Appearance.tsx b/src/new-frontend/src/components/UserSettings/Appearance.tsx index 9659dd158a7..b1f8fb70528 100644 --- a/src/new-frontend/src/components/UserSettings/Appearance.tsx +++ b/src/new-frontend/src/components/UserSettings/Appearance.tsx @@ -15,10 +15,10 @@ const Appearance: React.FC = () => { {/* TODO: Add system default option */} - Light ModeDefault + Light modeDefault - Dark Mode + Dark mode diff --git a/src/new-frontend/src/components/UserSettings/DeleteConfirmation.tsx b/src/new-frontend/src/components/UserSettings/DeleteConfirmation.tsx index e0edb53820b..6aa846a0e2b 100644 --- a/src/new-frontend/src/components/UserSettings/DeleteConfirmation.tsx +++ b/src/new-frontend/src/components/UserSettings/DeleteConfirmation.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { AlertDialog, AlertDialogBody, AlertDialogContent, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, Button } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; @@ -54,7 +54,7 @@ const DeleteConfirmation: React.FC = ({ isOpen, onClose }) => { - diff --git a/src/new-frontend/src/components/UserSettings/UserInformation.tsx b/src/new-frontend/src/components/UserSettings/UserInformation.tsx index de12474926f..04e2ff67f2e 100644 --- a/src/new-frontend/src/components/UserSettings/UserInformation.tsx +++ b/src/new-frontend/src/components/UserSettings/UserInformation.tsx @@ -75,7 +75,7 @@ const UserInformation: React.FC = () => { {editMode ? 'Save' : 'Edit'} {editMode && - } diff --git a/src/new-frontend/src/pages/Login.tsx b/src/new-frontend/src/pages/Login.tsx index 83053ddc3a0..4563c6db329 100644 --- a/src/new-frontend/src/pages/Login.tsx +++ b/src/new-frontend/src/pages/Login.tsx @@ -1,23 +1,35 @@ import React from 'react'; import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'; -import { Button, Center, Container, FormControl, Icon, Image, Input, InputGroup, InputRightElement, Link, useBoolean } from '@chakra-ui/react'; +import { Button, Center, Container, FormControl, FormErrorMessage, Icon, Image, Input, InputGroup, InputRightElement, Link, useBoolean } from '@chakra-ui/react'; import { SubmitHandler, useForm } from 'react-hook-form'; -import { Link as ReactRouterLink, useNavigate } from 'react-router-dom'; +import { Link as ReactRouterLink } from 'react-router-dom'; import Logo from '../assets/images/fastapi-logo.svg'; +import { ApiError } from '../client'; import { Body_login_login_access_token as AccessToken } from '../client/models/Body_login_login_access_token'; import useAuth from '../hooks/useAuth'; const Login: React.FC = () => { const [show, setShow] = useBoolean(); - const navigate = useNavigate(); - const { register, handleSubmit } = useForm(); + const [error, setError] = React.useState(null); + const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ + mode: 'onBlur', + criteriaMode: 'all', + defaultValues: { + username: '', + password: '' + } + }); const { login } = useAuth(); const onSubmit: SubmitHandler = async (data) => { - await login(data); - navigate('/'); + try { + await login(data); + } catch (err) { + const errDetail = (err as ApiError).body.detail; + setError(errDetail) + } }; return ( @@ -32,11 +44,12 @@ const Login: React.FC = () => { gap={4} centerContent > - FastAPI logo - - + FastAPI logo + + + {errors.username && {errors.username.message}} - + { -
- - Forgot password? - -
+ {error && + {error} + }
-