mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
Realtime page components (#40967)
* realtime page components * PRettier --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
4cd7c749a9
commit
4351c3b31b
3 files changed
+471
-461
No files matched your search
@@ -6,7 +6,6 @@ import { toast } from 'sonner'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { ScaffoldSection } from 'components/layouts/Scaffold'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
|
||||
import { ToggleSpendCapButton } from 'components/ui/ToggleSpendCapButton'
|
||||
@@ -145,446 +144,422 @@ export const RealtimeSettings = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScaffoldSection isFullWidth>
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
|
||||
{isError ? (
|
||||
<AlertError error={error} subject="Failed to retrieve realtime settings" />
|
||||
) : (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="suspend"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !pt-2"
|
||||
header={<FormSectionLabel>Enable Realtime service</FormSectionLabel>}
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
|
||||
{isError ? (
|
||||
<AlertError error={error} subject="Failed to retrieve realtime settings" />
|
||||
) : (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="suspend"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !pt-2"
|
||||
header={<FormSectionLabel>Enable Realtime service</FormSectionLabel>}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading || isLoadingPermissions}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading || isLoadingPermissions}
|
||||
className="!gap-y-2"
|
||||
<FormItemLayout
|
||||
layout="flex"
|
||||
label="Enable Realtime service"
|
||||
description="If disabled, no clients will be able to connect and new connections will be rejected"
|
||||
>
|
||||
<FormItemLayout
|
||||
layout="flex"
|
||||
label="Enable Realtime service"
|
||||
description="If disabled, no clients will be able to connect and new connections will be rejected"
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={!field.value}
|
||||
onCheckedChange={(checked) => field.onChange(!checked)}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
<FormMessage_Shadcn_ />
|
||||
{(isRealtimeDisabled || isDisablingRealtime || isEnablingRealtime) && (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type={isDisablingRealtime || isEnablingRealtime ? 'warning' : 'default'}
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={!field.value}
|
||||
onCheckedChange={(checked) => field.onChange(!checked)}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
<FormMessage_Shadcn_ />
|
||||
{(isRealtimeDisabled || isDisablingRealtime || isEnablingRealtime) && (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type={
|
||||
isDisablingRealtime || isEnablingRealtime ? 'warning' : 'default'
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
{isDisablingRealtime
|
||||
? 'Realtime service will be disabled'
|
||||
: isEnablingRealtime
|
||||
? 'Realtime service will be re-enabled'
|
||||
: isRealtimeDisabled
|
||||
? 'Realtime service is disabled'
|
||||
: null}
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isDisablingRealtime
|
||||
? 'Clients will no longer be able to connect to your project’s realtime service once saved'
|
||||
: isEnablingRealtime
|
||||
? "Clients will be able to connect to your project's realtime service again once saved"
|
||||
: isRealtimeDisabled
|
||||
? 'You will need to enable it to continue using Realtime'
|
||||
: null}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
{isDisablingRealtime
|
||||
? 'Realtime service will be disabled'
|
||||
: isEnablingRealtime
|
||||
? 'Realtime service will be re-enabled'
|
||||
: isRealtimeDisabled
|
||||
? 'Realtime service is disabled'
|
||||
: null}
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isDisablingRealtime
|
||||
? 'Clients will no longer be able to connect to your project’s realtime service once saved'
|
||||
: isEnablingRealtime
|
||||
? "Clients will be able to connect to your project's realtime service again once saved"
|
||||
: isRealtimeDisabled
|
||||
? 'You will need to enable it to continue using Realtime'
|
||||
: null}
|
||||
</p>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
{!suspend && (
|
||||
<>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="allow_public"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !pt-2"
|
||||
header={<FormSectionLabel>Channel restrictions</FormSectionLabel>}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading || isLoadingPermissions}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormItemLayout
|
||||
layout="flex"
|
||||
label="Allow public access"
|
||||
description="If disabled, only private channels will be allowed"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
|
||||
{isSuccessPolicies &&
|
||||
!hasRealtimeMessagesPolicies &&
|
||||
!allow_public &&
|
||||
!isRealtimeDisabled && (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type="warning"
|
||||
title="No Realtime RLS policies found"
|
||||
description={
|
||||
<>
|
||||
<p className="prose max-w-full text-sm">
|
||||
Private mode is {isSettingToPrivate ? 'being ' : ''}
|
||||
enabled, but no RLS policies exists on the{' '}
|
||||
<code className="text-code-inline">
|
||||
realtime.messages
|
||||
</code>{' '}
|
||||
table. No messages will be received by users.
|
||||
</p>
|
||||
|
||||
<Button asChild type="default" className="mt-2">
|
||||
<Link href={`/project/${projectRef}/realtime/policies`}>
|
||||
Create policy
|
||||
</Link>
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="connection_pool"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Realtime Authorization uses this database pool to check client
|
||||
access
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Database connection pool size
|
||||
</FormSectionLabel>
|
||||
}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
{!suspend && (
|
||||
<>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="allow_public"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !pt-2"
|
||||
header={<FormSectionLabel>Channel restrictions</FormSectionLabel>}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading || isLoadingPermissions}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading}
|
||||
className="!gap-y-2"
|
||||
<FormItemLayout
|
||||
layout="flex"
|
||||
label="Allow public access"
|
||||
description="If disabled, only private channels will be allowed"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{!!maxConn && field.value > maxConn.maxConnections * 0.5 && (
|
||||
</FormItemLayout>
|
||||
|
||||
{isSuccessPolicies &&
|
||||
!hasRealtimeMessagesPolicies &&
|
||||
!allow_public &&
|
||||
!isRealtimeDisabled && (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type="warning"
|
||||
title={`Pool size is greater than 50% of the max connections (${maxConn.maxConnections}) on your database`}
|
||||
description="This may result in instability and unreliability with your database connections."
|
||||
/>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_concurrent_users"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of concurrent clients that can connect to
|
||||
your Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max concurrent clients
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_events_per_second"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of events per second that can be sent to
|
||||
your Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max events per second
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!isUsageBillingEnabled || !canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{isSuccessOrganization && !isUsageBillingEnabled && (
|
||||
<Admonition showIcon={false} type="default">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
Spend cap needs to be disabled to configure this value
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isFreePlan
|
||||
? 'Upgrade to the Pro plan first to disable spend cap'
|
||||
: 'You may adjust this setting in the organization billing settings'}
|
||||
title="No Realtime RLS policies found"
|
||||
description={
|
||||
<>
|
||||
<p className="prose max-w-full text-sm">
|
||||
Private mode is {isSettingToPrivate ? 'being ' : ''}
|
||||
enabled, but no RLS policies exists on the{' '}
|
||||
<code className="text-code-inline">
|
||||
realtime.messages
|
||||
</code>{' '}
|
||||
table. No messages will be received by users.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-grow flex items-center justify-end">
|
||||
{isFreePlan ? (
|
||||
<UpgradePlanButton source="realtimeSettings" plan="Pro" />
|
||||
) : (
|
||||
<ToggleSpendCapButton />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_presence_events_per_second"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of presence events per second that can be
|
||||
sent to your Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max presence events per second
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!isUsageBillingEnabled || !canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{isSuccessOrganization && !isUsageBillingEnabled && (
|
||||
<Admonition showIcon={false} type="default">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
Spend cap needs to be disabled to configure this value
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isFreePlan
|
||||
? 'Upgrade to the Pro plan first to disable spend cap'
|
||||
: 'You may adjust this setting in the organization billing settings'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-grow flex items-center justify-end">
|
||||
{isFreePlan ? (
|
||||
<UpgradePlanButton source="realtimeSettings" plan="Pro" />
|
||||
) : (
|
||||
<ToggleSpendCapButton />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_payload_size_in_kb"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of payload size in KB that can be sent to
|
||||
your Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max payload size in KB
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent
|
||||
loaders={1}
|
||||
loading={isLoading}
|
||||
className="!gap-y-2"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!isUsageBillingEnabled || !canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{isSuccessOrganization && !isUsageBillingEnabled && (
|
||||
<Admonition showIcon={false} type="default">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
Spend cap needs to be disabled to configure this value
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isFreePlan
|
||||
? 'Upgrade to the Pro plan first to disable spend cap'
|
||||
: 'You may adjust this setting in the organization billing settings'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-grow flex items-center justify-end">
|
||||
{isFreePlan ? (
|
||||
<UpgradePlanButton source="realtimeSettings" plan="Pro" />
|
||||
) : (
|
||||
<ToggleSpendCapButton />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
</>
|
||||
)}
|
||||
|
||||
<CardFooter className="justify-between">
|
||||
<div>
|
||||
{isPermissionsLoaded && !canUpdateConfig && (
|
||||
<p className="text-sm text-foreground-light">
|
||||
You need additional permissions to update realtime settings
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{form.formState.isDirty && (
|
||||
<Button type="default" onClick={() => form.reset(data as any)}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
form={formId}
|
||||
disabled={!canUpdateConfig || isUpdatingConfig || !form.formState.isDirty}
|
||||
loading={isUpdatingConfig}
|
||||
>
|
||||
Save changes
|
||||
<Button asChild type="default" className="mt-2">
|
||||
<Link href={`/project/${projectRef}/realtime/policies`}>
|
||||
Create policy
|
||||
</Link>
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="connection_pool"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Realtime Authorization uses this database pool to check client
|
||||
access
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Database connection pool size
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{!!maxConn && field.value > maxConn.maxConnections * 0.5 && (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type="warning"
|
||||
title={`Pool size is greater than 50% of the max connections (${maxConn.maxConnections}) on your database`}
|
||||
description="This may result in instability and unreliability with your database connections."
|
||||
/>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_concurrent_users"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of concurrent clients that can connect to your
|
||||
Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max concurrent clients
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_events_per_second"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of events per second that can be sent to your
|
||||
Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max events per second
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!isUsageBillingEnabled || !canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{isSuccessOrganization && !isUsageBillingEnabled && (
|
||||
<Admonition showIcon={false} type="default">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
Spend cap needs to be disabled to configure this value
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isFreePlan
|
||||
? 'Upgrade to the Pro plan first to disable spend cap'
|
||||
: 'You may adjust this setting in the organization billing settings'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-grow flex items-center justify-end">
|
||||
{isFreePlan ? (
|
||||
<UpgradePlanButton source="realtimeSettings" plan="Pro" />
|
||||
) : (
|
||||
<ToggleSpendCapButton />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_presence_events_per_second"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of presence events per second that can be sent
|
||||
to your Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max presence events per second
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!isUsageBillingEnabled || !canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{isSuccessOrganization && !isUsageBillingEnabled && (
|
||||
<Admonition showIcon={false} type="default">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
Spend cap needs to be disabled to configure this value
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isFreePlan
|
||||
? 'Upgrade to the Pro plan first to disable spend cap'
|
||||
: 'You may adjust this setting in the organization billing settings'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-grow flex items-center justify-end">
|
||||
{isFreePlan ? (
|
||||
<UpgradePlanButton source="realtimeSettings" plan="Pro" />
|
||||
) : (
|
||||
<ToggleSpendCapButton />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="max_payload_size_in_kb"
|
||||
render={({ field }) => (
|
||||
<FormSection
|
||||
className="!p-0 !py-2"
|
||||
header={
|
||||
<FormSectionLabel
|
||||
description={
|
||||
<p className="text-foreground-lighter text-sm !mt-1">
|
||||
Sets maximum number of payload size in KB that can be sent to your
|
||||
Realtime service
|
||||
</p>
|
||||
}
|
||||
>
|
||||
Max payload size in KB
|
||||
</FormSectionLabel>
|
||||
}
|
||||
>
|
||||
<FormSectionContent loaders={1} loading={isLoading} className="!gap-y-2">
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
{...field}
|
||||
type="number"
|
||||
disabled={!isUsageBillingEnabled || !canUpdateConfig}
|
||||
value={field.value || ''}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<FormMessage_Shadcn_ />
|
||||
{isSuccessOrganization && !isUsageBillingEnabled && (
|
||||
<Admonition showIcon={false} type="default">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div>
|
||||
<h5 className="text-foreground mb-1">
|
||||
Spend cap needs to be disabled to configure this value
|
||||
</h5>
|
||||
<p className="text-foreground-light">
|
||||
{isFreePlan
|
||||
? 'Upgrade to the Pro plan first to disable spend cap'
|
||||
: 'You may adjust this setting in the organization billing settings'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-grow flex items-center justify-end">
|
||||
{isFreePlan ? (
|
||||
<UpgradePlanButton source="realtimeSettings" plan="Pro" />
|
||||
) : (
|
||||
<ToggleSpendCapButton />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
)}
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
</>
|
||||
)}
|
||||
|
||||
<CardFooter className="justify-between">
|
||||
<div>
|
||||
{isPermissionsLoaded && !canUpdateConfig && (
|
||||
<p className="text-sm text-foreground-light">
|
||||
You need additional permissions to update realtime settings
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{form.formState.isDirty && (
|
||||
<Button type="default" onClick={() => form.reset(data as any)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)}
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</ScaffoldSection>
|
||||
)}
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
form={formId}
|
||||
disabled={!canUpdateConfig || isUpdatingConfig || !form.formState.isDirty}
|
||||
loading={isUpdatingConfig}
|
||||
>
|
||||
Save changes
|
||||
</Button>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)}
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
|
||||
<ConfirmationModal
|
||||
visible={isConfirmNextModalOpen}
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
import { RealtimePolicies } from 'components/interfaces/Realtime/Policies'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
import RealtimeLayout from 'components/layouts/RealtimeLayout/RealtimeLayout'
|
||||
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { DOCS_URL } from 'lib/constants'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import {
|
||||
PageHeader,
|
||||
PageHeaderAside,
|
||||
PageHeaderDescription,
|
||||
PageHeaderMeta,
|
||||
PageHeaderSummary,
|
||||
PageHeaderTitle,
|
||||
} from 'ui-patterns/PageHeader'
|
||||
import { PageSection, PageSectionContent } from 'ui-patterns/PageSection'
|
||||
|
||||
const RealtimePoliciesPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<ScaffoldContainer size="full">
|
||||
<ScaffoldSection isFullWidth>
|
||||
<RealtimePolicies />
|
||||
</ScaffoldSection>
|
||||
</ScaffoldContainer>
|
||||
<>
|
||||
<PageHeader size="large">
|
||||
<PageHeaderMeta>
|
||||
<PageHeaderSummary>
|
||||
<PageHeaderTitle>Policies</PageHeaderTitle>
|
||||
<PageHeaderDescription>Control access to your realtime channels</PageHeaderDescription>
|
||||
</PageHeaderSummary>
|
||||
<PageHeaderAside>
|
||||
<DocsButton href={`${DOCS_URL}/guides/realtime/authorization`} />
|
||||
</PageHeaderAside>
|
||||
</PageHeaderMeta>
|
||||
</PageHeader>
|
||||
<PageContainer size="large">
|
||||
<PageSection>
|
||||
<PageSectionContent>
|
||||
<RealtimePolicies />
|
||||
</PageSectionContent>
|
||||
</PageSection>
|
||||
</PageContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
RealtimePoliciesPage.getLayout = (page) => (
|
||||
<DefaultLayout>
|
||||
<RealtimeLayout title="Policies">
|
||||
<PageLayout
|
||||
title="Policies"
|
||||
subtitle="Control access to your realtime channels"
|
||||
primaryActions={<DocsButton href={`${DOCS_URL}/guides/realtime/authorization`} />}
|
||||
size="large"
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
</RealtimeLayout>
|
||||
<RealtimeLayout title="Policies">{page}</RealtimeLayout>
|
||||
</DefaultLayout>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,29 +1,51 @@
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
import { RealtimeSettings } from 'components/interfaces/Realtime/RealtimeSettings'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
import RealtimeLayout from 'components/layouts/RealtimeLayout/RealtimeLayout'
|
||||
import { ScaffoldContainer } from 'components/layouts/Scaffold'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { DOCS_URL } from 'lib/constants'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { RealtimeSettings } from 'components/interfaces/Realtime/RealtimeSettings'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import {
|
||||
PageHeader,
|
||||
PageHeaderAside,
|
||||
PageHeaderDescription,
|
||||
PageHeaderMeta,
|
||||
PageHeaderSummary,
|
||||
PageHeaderTitle,
|
||||
} from 'ui-patterns/PageHeader'
|
||||
import { PageSection, PageSectionContent } from 'ui-patterns/PageSection'
|
||||
|
||||
const RealtimePoliciesPage: NextPageWithLayout = () => {
|
||||
return <RealtimeSettings />
|
||||
const RealtimeSettingsPage: NextPageWithLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<PageHeader size="large">
|
||||
<PageHeaderMeta>
|
||||
<PageHeaderSummary>
|
||||
<PageHeaderTitle>Realtime Settings</PageHeaderTitle>
|
||||
<PageHeaderDescription>
|
||||
Configure your project's Realtime settings
|
||||
</PageHeaderDescription>
|
||||
</PageHeaderSummary>
|
||||
<PageHeaderAside>
|
||||
<DocsButton href={`${DOCS_URL}/guides/realtime/settings`} />
|
||||
</PageHeaderAside>
|
||||
</PageHeaderMeta>
|
||||
</PageHeader>
|
||||
<PageContainer size="large">
|
||||
<PageSection>
|
||||
<PageSectionContent>
|
||||
<RealtimeSettings />
|
||||
</PageSectionContent>
|
||||
</PageSection>
|
||||
</PageContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
RealtimePoliciesPage.getLayout = (page) => (
|
||||
RealtimeSettingsPage.getLayout = (page) => (
|
||||
<DefaultLayout>
|
||||
<RealtimeLayout title="Realtime Settings">
|
||||
<PageLayout
|
||||
title="Realtime Settings"
|
||||
subtitle="Configure your project's Realtime settings"
|
||||
primaryActions={<DocsButton href={`${DOCS_URL}/guides/realtime/settings`} />}
|
||||
>
|
||||
<ScaffoldContainer>{page}</ScaffoldContainer>
|
||||
</PageLayout>
|
||||
</RealtimeLayout>
|
||||
<RealtimeLayout title="Realtime Settings">{page}</RealtimeLayout>
|
||||
</DefaultLayout>
|
||||
)
|
||||
|
||||
export default RealtimePoliciesPage
|
||||
export default RealtimeSettingsPage
|
||||
Reference in new issue
Block a user