11import {
2- Box ,
32 Button ,
4- Flex ,
53 FormControl ,
6- FormErrorMessage ,
74 FormLabel ,
85 Input ,
6+ Modal ,
7+ ModalBody ,
8+ ModalCloseButton ,
9+ ModalContent ,
10+ ModalFooter ,
11+ ModalHeader ,
12+ ModalOverlay ,
913 Text ,
1014} from "@chakra-ui/react"
1115import { useMutation , useQueryClient } from "@tanstack/react-query"
1216import { type SubmitHandler , useForm } from "react-hook-form"
1317
14- import { type InvitationCreate , InvitationsService } from "../../client"
15- import useCustomToast from "../../hooks/useCustomToast"
18+ import { useState } from "react"
19+ import {
20+ type ApiError ,
21+ type InvitationCreate ,
22+ InvitationsService ,
23+ } from "../../client"
1624import { Route } from "../../routes/_layout/$team"
17- import { emailPattern , handleError } from "../../utils"
25+ import { emailPattern , extractErrorMessage } from "../../utils"
26+
27+ interface NewInvitationProps {
28+ isOpen : boolean
29+ onClose : ( ) => void
30+ }
1831
19- const NewInvitation = ( ) => {
32+ const NewInvitation = ( { isOpen, onClose } : NewInvitationProps ) => {
33+ const [ status , setStatus ] = useState <
34+ "idle" | "loading" | "success" | "error"
35+ > ( "idle" )
2036 const { team : teamSlug } = Route . useParams ( )
2137 const queryClient = useQueryClient ( )
22- const showToast = useCustomToast ( )
2338 const {
2439 register,
2540 handleSubmit,
@@ -34,16 +49,19 @@ const NewInvitation = () => {
3449 mutationFn : ( data : InvitationCreate ) =>
3550 InvitationsService . createInvitation ( { requestBody : data } ) ,
3651 onSuccess : ( ) => {
37- showToast ( "Success!" , "Invitation sent successfully." , "success" )
52+ setStatus ( "success" )
3853 reset ( )
3954 } ,
40- onError : handleError . bind ( showToast ) ,
55+ onError : ( ) => {
56+ setStatus ( "error" )
57+ } ,
4158 onSettled : ( ) => {
4259 queryClient . invalidateQueries ( { queryKey : [ "invitations" ] } )
4360 } ,
4461 } )
4562
4663 const onSubmit : SubmitHandler < InvitationCreate > = ( data ) => {
64+ setStatus ( "loading" )
4765 const updatedData : InvitationCreate = {
4866 ...data ,
4967 role : "member" ,
@@ -52,43 +70,101 @@ const NewInvitation = () => {
5270 mutation . mutate ( updatedData )
5371 }
5472
73+ const handleClose = ( ) => {
74+ setStatus ( "idle" )
75+ onClose ( )
76+ }
77+
5578 return (
56- < Flex
57- textAlign = { { base : "center" , md : "left" } }
58- flexDirection = "column"
59- alignItems = "center"
60- justifyContent = "center"
79+ < Modal
80+ isOpen = { isOpen }
81+ onClose = { handleClose }
82+ size = { { base : "sm" , md : "md" } }
83+ isCentered
6184 >
62- < Box
85+ < ModalOverlay />
86+ < ModalContent
6387 as = "form"
6488 onSubmit = { handleSubmit ( onSubmit ) }
6589 data-testid = "new-invitation"
6690 >
67- < Text mb = { 4 } > Invite someone to join your team.</ Text >
68- < FormControl isRequired isInvalid = { ! ! errors . email } >
69- < FormLabel htmlFor = "email" hidden >
70- Email address
71- </ FormLabel >
72- < Input
73- id = "email"
74- { ...register ( "email" , {
75- required : "Email is required" ,
76- pattern : emailPattern ,
77- } ) }
78- placeholder = "Email address"
79- type = "text"
80- w = "auto"
81- data-testid = "invitation-email"
82- />
83- { errors . email && (
84- < FormErrorMessage > { errors . email . message } </ FormErrorMessage >
85- ) }
86- </ FormControl >
87- < Button variant = "primary" type = "submit" isLoading = { isSubmitting } mt = { 4 } >
88- Send invitation
89- </ Button >
90- </ Box >
91- </ Flex >
91+ { status === "idle" || status === "loading" ? (
92+ < >
93+ < ModalHeader > Team Invitation</ ModalHeader >
94+ < ModalCloseButton aria-label = "Close invitation modal" />
95+ < ModalBody >
96+ < Text mb = { 4 } > Invite someone to your team</ Text >
97+ < FormControl isRequired isInvalid = { ! ! errors . email } >
98+ < FormLabel htmlFor = "email" hidden >
99+ Email address
100+ </ FormLabel >
101+ < Input
102+ id = "email"
103+ { ...register ( "email" , {
104+ required : "Email is required" ,
105+ pattern : emailPattern ,
106+ } ) }
107+ placeholder = "Email address"
108+ type = "text"
109+ data-testid = "invitation-email"
110+ />
111+ { errors . email && (
112+ < Text id = "email-error" color = "red.500" mt = { 2 } >
113+ { errors . email . message }
114+ </ Text >
115+ ) }
116+ </ FormControl >
117+ </ ModalBody >
118+ < ModalFooter >
119+ < Button
120+ variant = "secondary"
121+ type = "submit"
122+ isLoading = { isSubmitting }
123+ mt = { 4 }
124+ >
125+ Send invitation
126+ </ Button >
127+ </ ModalFooter >
128+ </ >
129+ ) : status === "success" ? (
130+ < >
131+ < ModalHeader > Success!</ ModalHeader >
132+ < ModalCloseButton aria-label = "Close invitation modal" />
133+ < ModalBody >
134+ < Text >
135+ The invitation has been sent to < b > { mutation . data ?. email } </ b > { " " }
136+ successfully. Now they just need to accept it.
137+ </ Text >
138+ </ ModalBody >
139+ < ModalFooter >
140+ < Button onClick = { handleClose } mt = { 4 } >
141+ Ok
142+ </ Button >
143+ </ ModalFooter >
144+ </ >
145+ ) : (
146+ < >
147+ < ModalHeader > Error</ ModalHeader >
148+ < ModalCloseButton aria-label = "Close invitation modal" />
149+ < ModalBody >
150+ < Text >
151+ An error occurred while sending the invitation. Please try
152+ again.
153+ </ Text >
154+
155+ < Text color = "red.500" mt = { 2 } >
156+ { extractErrorMessage ( mutation . error as ApiError ) }
157+ </ Text >
158+ </ ModalBody >
159+ < ModalFooter >
160+ < Button onClick = { handleClose } mt = { 4 } >
161+ Ok
162+ </ Button >
163+ </ ModalFooter >
164+ </ >
165+ ) }
166+ </ ModalContent >
167+ </ Modal >
92168 )
93169}
94170
0 commit comments