SiteUrl.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { zodResolver } from '@hookform/resolvers/zod'
  2. import { PermissionAction } from '@supabase/shared-types/out/constants'
  3. import { useParams } from 'common'
  4. import { useEffect, useState } from 'react'
  5. import { useForm } from 'react-hook-form'
  6. import { toast } from 'sonner'
  7. import { Button, Card, CardContent, CardFooter, Form, FormControl, FormField, Input } from 'ui'
  8. import { GenericSkeletonLoader } from 'ui-patterns'
  9. import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
  10. import {
  11. PageSection,
  12. PageSectionContent,
  13. PageSectionMeta,
  14. PageSectionSummary,
  15. PageSectionTitle,
  16. } from 'ui-patterns/PageSection'
  17. import * as z from 'zod'
  18. import AlertError from '@/components/ui/AlertError'
  19. import { useAuthConfigQuery } from '@/data/auth/auth-config-query'
  20. import { useAuthConfigUpdateMutation } from '@/data/auth/auth-config-update-mutation'
  21. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  22. const schema = z.object({
  23. SITE_URL: z.string().min(1, 'Must have a Site URL'),
  24. })
  25. const SiteUrl = () => {
  26. const { ref: projectRef } = useParams()
  27. const {
  28. data: authConfig,
  29. error: authConfigError,
  30. isError,
  31. isPending: isLoading,
  32. } = useAuthConfigQuery({ projectRef })
  33. const { mutate: updateAuthConfig } = useAuthConfigUpdateMutation()
  34. const [isUpdatingSiteUrl, setIsUpdatingSiteUrl] = useState(false)
  35. const { can: canUpdateConfig } = useAsyncCheckPermissions(
  36. PermissionAction.UPDATE,
  37. 'custom_config_gotrue'
  38. )
  39. const siteUrlForm = useForm({
  40. resolver: zodResolver(schema as any),
  41. defaultValues: {
  42. SITE_URL: '',
  43. },
  44. })
  45. const { isDirty } = siteUrlForm.formState
  46. useEffect(() => {
  47. if (authConfig && !isUpdatingSiteUrl) {
  48. siteUrlForm.reset({
  49. SITE_URL: authConfig.SITE_URL || '',
  50. })
  51. }
  52. }, [authConfig, isUpdatingSiteUrl])
  53. const onSubmitSiteUrl = (values: any) => {
  54. setIsUpdatingSiteUrl(true)
  55. updateAuthConfig(
  56. { projectRef: projectRef!, config: values },
  57. {
  58. onError: (error) => {
  59. toast.error(`Failed to update site URL: ${error?.message}`)
  60. setIsUpdatingSiteUrl(false)
  61. },
  62. onSuccess: () => {
  63. toast.success('Successfully updated site URL')
  64. setIsUpdatingSiteUrl(false)
  65. },
  66. }
  67. )
  68. }
  69. if (isError) {
  70. return (
  71. <PageSection>
  72. <PageSectionContent>
  73. <AlertError error={authConfigError} subject="Failed to retrieve auth configuration" />
  74. </PageSectionContent>
  75. </PageSection>
  76. )
  77. }
  78. if (isLoading) {
  79. return (
  80. <PageSection>
  81. <PageSectionContent>
  82. <GenericSkeletonLoader />
  83. </PageSectionContent>
  84. </PageSection>
  85. )
  86. }
  87. return (
  88. <PageSection>
  89. <PageSectionMeta>
  90. <PageSectionSummary>
  91. <PageSectionTitle>Site URL</PageSectionTitle>
  92. </PageSectionSummary>
  93. </PageSectionMeta>
  94. <PageSectionContent>
  95. <Form {...siteUrlForm}>
  96. <form onSubmit={siteUrlForm.handleSubmit(onSubmitSiteUrl)}>
  97. <Card>
  98. <CardContent>
  99. <FormField
  100. control={siteUrlForm.control}
  101. name="SITE_URL"
  102. render={({ field }) => (
  103. <FormItemLayout
  104. layout="flex-row-reverse"
  105. label="Site URL"
  106. description="Configure the default redirect URL used when a redirect URL is not specified or doesn't match one from the allow list. This value is also exposed as a template variable in the email templates section. Wildcards cannot be used here."
  107. >
  108. <FormControl>
  109. <Input {...field} disabled={!canUpdateConfig} />
  110. </FormControl>
  111. </FormItemLayout>
  112. )}
  113. />
  114. </CardContent>
  115. <CardFooter className="justify-end space-x-2">
  116. {isDirty && (
  117. <Button type="default" onClick={() => siteUrlForm.reset()}>
  118. Cancel
  119. </Button>
  120. )}
  121. <Button
  122. type="primary"
  123. htmlType="submit"
  124. disabled={!canUpdateConfig || isUpdatingSiteUrl || !isDirty}
  125. loading={isUpdatingSiteUrl}
  126. >
  127. Save changes
  128. </Button>
  129. </CardFooter>
  130. </Card>
  131. </form>
  132. </Form>
  133. </PageSectionContent>
  134. </PageSection>
  135. )
  136. }
  137. export default SiteUrl