ChangeEmailAddress.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import HCaptcha from '@hcaptcha/react-hcaptcha'
  2. import { zodResolver } from '@hookform/resolvers/zod'
  3. import { useRef, useState } from 'react'
  4. import { SubmitHandler, useForm } from 'react-hook-form'
  5. import { toast } from 'sonner'
  6. import { Button, DialogFooter, DialogSection, Form, FormControl, FormField, Input } from 'ui'
  7. import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
  8. import * as z from 'zod'
  9. import { InlineLink } from '@/components/ui/InlineLink'
  10. import { useEmailUpdateMutation } from '@/data/profile/profile-update-email-mutation'
  11. export const GitHubChangeEmailAddress = () => {
  12. return (
  13. <DialogSection className="flex flex-col gap-y-2">
  14. <p className="text-sm">
  15. Email addresses for GitHub identities should be updated through GitHub
  16. </p>
  17. <ol className="flex flex-col gap-y-0.5 text-sm ml-4 pl-2 list-decimal text-foreground-light">
  18. <li>Log out of Briven</li>
  19. <li>
  20. Change your Primary Email in{' '}
  21. <InlineLink href="https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/changing-your-primary-email-address">
  22. GitHub
  23. </InlineLink>{' '}
  24. (your primary email)
  25. </li>
  26. <li>Log out of GitHub</li>
  27. <li>Log back into GitHub (with the new, desired email set as primary)</li>
  28. <li>Log back into Briven</li>
  29. </ol>
  30. </DialogSection>
  31. )
  32. }
  33. export const SSOChangeEmailAddress = () => {
  34. return (
  35. <DialogSection className="flex flex-col gap-y-2">
  36. <p className="text-sm">
  37. Email addresses for SSO should be updated through your identity provider
  38. </p>
  39. <ol className="flex flex-col gap-y-0.5 text-sm ml-4 pl-2 list-decimal text-foreground-light">
  40. <li>Contact the owner / admin for your team to change your email</li>
  41. </ol>
  42. </DialogSection>
  43. )
  44. }
  45. export const ChangeEmailAddressForm = ({ onClose }: { onClose: () => void }) => {
  46. const captchaRef = useRef<HCaptcha>(null)
  47. const [captchaToken, setCaptchaToken] = useState<string | null>(null)
  48. const FormSchema = z.object({ email: z.string().email() })
  49. const form = useForm<z.infer<typeof FormSchema>>({
  50. mode: 'onBlur',
  51. reValidateMode: 'onBlur',
  52. resolver: zodResolver(FormSchema as any),
  53. defaultValues: { email: '' },
  54. })
  55. const { mutate: updateEmail, isPending } = useEmailUpdateMutation({
  56. onSuccess: (_, vars) => {
  57. toast.success(
  58. `A confirmation email has been sent to ${vars.email}. Please confirm the change within 10 minutes.`
  59. )
  60. onClose()
  61. },
  62. onError: (error) => {
  63. toast.error(`Failed to update email: ${error.message}`)
  64. setCaptchaToken(null)
  65. captchaRef.current?.resetCaptcha()
  66. },
  67. })
  68. const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (values) => {
  69. let token = captchaToken
  70. if (!token) {
  71. const captchaResponse = await captchaRef.current?.execute({ async: true })
  72. token = captchaResponse?.response ?? null
  73. }
  74. updateEmail({ email: values.email, hcaptchaToken: token ?? null })
  75. }
  76. return (
  77. <Form {...form}>
  78. <form id="update-email-form" onSubmit={form.handleSubmit(onSubmit)}>
  79. <div className="self-center">
  80. <HCaptcha
  81. ref={captchaRef}
  82. sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
  83. size="invisible"
  84. onVerify={(token) => setCaptchaToken(token)}
  85. onExpire={() => setCaptchaToken(null)}
  86. />
  87. </div>
  88. <DialogSection>
  89. <FormField
  90. name="email"
  91. control={form.control}
  92. render={({ field }) => (
  93. <FormItemLayout
  94. label="Provide a new email address"
  95. description="A confirmation email will be sent to the provided email address"
  96. >
  97. <FormControl>
  98. <Input {...field} placeholder="example@email.com" />
  99. </FormControl>
  100. </FormItemLayout>
  101. )}
  102. />
  103. </DialogSection>
  104. <DialogFooter>
  105. <Button type="default" disabled={isPending} onClick={onClose}>
  106. Cancel
  107. </Button>
  108. <Button htmlType="submit" loading={isPending} disabled={isPending}>
  109. Confirm
  110. </Button>
  111. </DialogFooter>
  112. </form>
  113. </Form>
  114. )
  115. }