Home / Function/ BillingEmail() — supabase Function Reference

BillingEmail() — supabase Function Reference

Architecture documentation for the BillingEmail() function in BillingEmail.tsx from the supabase codebase.

Entity Profile

Relationship Graph

Source Code

apps/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx lines 39–213

const BillingEmail = () => {
  const { slug } = useParams()
  const { data: selectedOrganization } = useSelectedOrganizationQuery()

  const { name, billing_email } = selectedOrganization ?? {}

  const { can: canReadBillingEmail, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions(
    PermissionAction.BILLING_READ,
    'stripe.subscriptions'
  )
  const { can: canUpdateOrganization } = useAsyncCheckPermissions(
    PermissionAction.UPDATE,
    'organizations'
  )

  const { data: billingCustomer, isPending: loadingBillingCustomer } =
    useOrganizationCustomerProfileQuery({ slug }, { enabled: canReadBillingEmail })

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      billingEmail: billing_email ?? '',
      additionalBillingEmails: billingCustomer?.additional_emails ?? [],
    },
  })
  const { additionalBillingEmails } = form.watch()
  const { errors } = form.formState
  const additionalEmailsError = errors.additionalBillingEmails ?? []

  const { mutate: updateOrganization, isPending: isUpdating } = useOrganizationUpdateMutation()

  const onUpdateOrganizationEmail = async (values: z.infer<typeof formSchema>) => {
    if (!canUpdateOrganization) {
      return toast.error('You do not have the required permissions to update this organization')
    }
    if (!slug) return console.error('Slug is required')
    if (!name) return console.error('Organization name is required')

    updateOrganization(
      {
        slug,
        name,
        billing_email: values.billingEmail,
        additional_billing_emails: values.additionalBillingEmails,
      },
      {
        onSuccess: () => {
          toast.success('Successfully saved settings')
          form.reset(values)
        },
      }
    )
  }

  useEffect(() => {
    if (billingCustomer) {
      form.reset({
        billingEmail: billing_email ?? '',
        additionalBillingEmails: billingCustomer.additional_emails ?? [],
      })
    }
  }, [billingCustomer])

  return (
    <ScaffoldSection>
      <ScaffoldSectionDetail>
        <div className="sticky space-y-2 top-12">
          <p className="text-foreground text-base m-0">Email Recipient</p>
          <p className="text-sm text-foreground-light m-0">
            All billing correspondence will go to this email
          </p>
        </div>
      </ScaffoldSectionDetail>
      <ScaffoldSectionContent>
        {isPermissionsLoaded && !canReadBillingEmail ? (
          <NoPermission resourceText="view this organization's email recipients" />
        ) : (
          <Form {...form}>
            <form id={FORM_ID} onSubmit={form.handleSubmit(onUpdateOrganizationEmail)}>
              <FormPanel
                footer={
                  <div className="flex py-4 px-[var(--card-padding-x)]">
                    <FormActions
                      form={FORM_ID}
                      isSubmitting={isUpdating}
                      hasChanges={form.formState.isDirty}
                      handleReset={form.reset}
                      disabled={!canUpdateOrganization}
                      helper={
                        !canUpdateOrganization
                          ? 'You need additional permissions to update billing emails'
                          : undefined
                      }
                    />
                  </div>
                }
              >
                <FormSection>
                  <FormSectionContent fullWidth loading={loadingBillingCustomer}>
                    <FormField
                      control={form.control}
                      name="billingEmail"
                      render={({ field }) => (
                        <FormItemLayout label="Email address">
                          <FormControl>
                            <Input_Shadcn_
                              type="email"
                              {...field}
                              placeholder="Email"
                              disabled={!canUpdateOrganization}
                            />
                          </FormControl>
                          <FormMessage_Shadcn_ />
                        </FormItemLayout>
                      )}
                    />

                    <FormField
                      control={form.control}
                      name="additionalBillingEmails"
                      render={({ field }) => (
                        <FormItemLayout
                          hideMessage
                          label={
                            <div className="flex items-center gap-x-1">
                              <span>Additional emails</span>
                              <InfoTooltip side="bottom">
                                These email addresses will be CC'd in automated invoice or payment
                                failure emails. Payment receipts will still only go to the primary
                                billing address.
                              </InfoTooltip>
                            </div>
                          }
                        >
                          <FormControl>
                            <MultiSelector values={field.value} onValuesChange={field.onChange}>
                              <MultiSelectorTrigger
                                deletableBadge
                                showIcon={false}
                                mode="inline-combobox"
                                label="Add additional recipients"
                                badgeLimit="wrap"
                              />
                              <MultiSelectorContent>
                                <MultiSelectorList creatable />
                              </MultiSelectorContent>
                            </MultiSelector>
                          </FormControl>
                          {/* [Joshen] Manually construct the message here as MultiSelector doesn't handle array errors from RHF atm */}
                          {Array.isArray(additionalEmailsError) &&
                            additionalEmailsError.length > 0 && (
                              <div className="flex flex-col gap-y-1 mt-2">
                                {additionalEmailsError.map((x, idx) => (
                                  <p
                                    key={`email-error-${idx}`}
                                    className="text-sm text-destructive"
                                  >
                                    "{additionalBillingEmails[idx]}" is not a valid email address
                                  </p>
                                ))}
                              </div>
                            )}
                        </FormItemLayout>
                      )}
                    />
                  </FormSectionContent>
                </FormSection>
              </FormPanel>
            </form>
          </Form>
        )}
      </ScaffoldSectionContent>
    </ScaffoldSection>
  )
}

Subdomains

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free