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>
)
}
Domain
Subdomains
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free