Update all documentation buttons (#30163)

This commit is contained in:
Joshen Lim authored and GitHub committed 2024-10-31 13:58:17 +08:00
1 parent 996f01fdd6
commit 585477752a
70 files changed
+916 -1649

No files matched your search

@@ -7,6 +7,7 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import type { components } from 'data/api'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
@@ -286,11 +287,7 @@ const ProviderForm = ({ config, provider }: ProviderFormProps) => {
</>
)}
<div className="flex items-center justify-between">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link href={provider.link} target="_blank" rel="noreferrer">
Documentation
</Link>
</Button>
<DocsButton href={provider.link} />
<div className="flex items-center gap-x-3">
<Button
type="default"
@@ -1,12 +1,11 @@
import { ExternalLink } from 'lucide-react'
import { useParams } from 'common'
import AlertError from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { Button, Tabs } from 'ui'
import { Tabs } from 'ui'
import { TEMPLATES_SCHEMAS } from '../AuthTemplatesValidation'
import EmailRateLimitsAlert from '../EmailRateLimitsAlert'
import TemplateEditor from './TemplateEditor'
@@ -34,15 +33,7 @@ const EmailTemplates = () => {
description="Customize the emails that will be sent out to your users."
/>
<div className="mb-6">
<Button type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/auth/auth-email-templates"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/auth/auth-email-templates" />
</div>
</div>
{isError && (
@@ -1,5 +1,7 @@
import { noop } from 'lodash'
import { ChevronLeft, ExternalLink, FlaskConical } from 'lucide-react'
import { ChevronLeft, FlaskConical } from 'lucide-react'
import { DocsButton } from 'components/ui/DocsButton'
import { Button } from 'ui'
import { POLICY_MODAL_VIEWS } from '../Policies.constants'
@@ -57,16 +59,10 @@ const PolicyEditorModalTitle = ({
Try Supabase Assistant
</Button>
)}
<Button asChild type="default" icon={<ExternalLink size={14} />} className="mt-[-4px]">
<a
href="https://supabase.com/docs/learn/auth-deep-dive/auth-policies"
target="_blank"
rel="noreferrer"
>
{' '}
Documentation
</a>
</Button>
<DocsButton
className="mt-[-4px]"
href="https://supabase.com/docs/learn/auth-deep-dive/auth-policies"
/>
</div>
</div>
)
@@ -26,7 +26,7 @@ const PolicyReview = ({
return (
<>
<Modal.Content>
<div className="space-y-6 py-8">
<div className="space-y-6">
<div className="flex items-center justify-between space-y-8">
<div className="flex flex-col">
<p className="text-sm text-foreground-light">
@@ -1,9 +1,8 @@
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { useMemo, useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { HorizontalShimmerWithIcon } from 'components/ui/Shimmers/Shimmers'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
@@ -71,22 +70,13 @@ export const RedirectUrls = () => {
return (
<div>
<div className="flex items-center justify-between">
<div className="flex items-center justify-between mb-6">
<FormHeader
className="mb-0"
title="Redirect URLs"
description="URLs that auth providers are permitted to redirect to post authentication. Wildcards are allowed, for example, https://*.domain.com"
/>
<div className="flex items-center gap-2 mb-6 ml-12">
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/auth/concepts/redirect-urls"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
</div>
<DocsButton href="https://supabase.com/docs/guides/auth/concepts/redirect-urls" />
</div>
{isLoading && (
<>
@@ -1,16 +1,5 @@
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { Button, Modal } from 'ui'
import {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from 'ui'
import { DocsButton } from 'components/ui/DocsButton'
import { Button, Modal, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
interface SpendCapModalProps {
visible: boolean
@@ -26,11 +15,7 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => {
header={
<div className="flex justify-between items-center">
<span>Spend Cap</span>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a target="_blank" href="https://supabase.com/docs/guides/platform/spend-cap">
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/spend-cap" />
</div>
}
showCloseButton={false}
@@ -10,6 +10,7 @@ import { useParams } from 'common'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import NoPermission from 'components/ui/NoPermission'
import { useBranchDeleteMutation } from 'data/branches/branch-delete-mutation'
import { useBranchesDisableMutation } from 'data/branches/branches-disable-mutation'
@@ -190,25 +191,21 @@ const BranchManagement = () => {
All branches
</Button>
</div>
<div className="flex items-center justify-between space-x-2">
<div className="flex items-center justify-between gap-x-2">
<Button
type={'text'}
icon={<MessageCircle className="text-muted" strokeWidth={1} />}
asChild
type="text"
icon={<MessageCircle className="text-muted" strokeWidth={1} />}
>
<a href="https://github.com/orgs/supabase/discussions/18937" target="_blank">
<a
target="_blank"
rel="noreferrer"
href="https://github.com/orgs/supabase/discussions/18937"
>
Branching Feedback
</a>
</Button>
<Button type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/platform/branching"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/branching" />
<ButtonTooltip
type="primary"
disabled={!canCreateBranches}
@@ -162,9 +162,6 @@ const PITRSelection = () => {
</AlertDescription_Shadcn_>
<div className="flex items-center gap-x-2 mt-2">
{/* [Joshen] Ideally we have some links to a docs to explain why so */}
{/* <Button type="default" icon={<ExternalLink strokeWidth={1.5} />}>
Documentation
</Button> */}
<Button type="default">
<Link href={`/project/${ref}/settings/infrastructure`}>
Infrastructure settings
@@ -4,6 +4,7 @@ import { useState } from 'react'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
import SchemaSelector from 'components/ui/SchemaSelector'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useSchemasQuery } from 'data/database/schemas-query'
@@ -59,28 +60,33 @@ const EnumeratedTypes = () => {
return (
<div className="space-y-4">
<div className="flex items-center gap-2 flex-wrap">
<SchemaSelector
className="w-[260px]"
size="small"
showError={false}
selectedSchemaName={selectedSchema}
onSelectSchema={setSelectedSchema}
/>
<Input
size="small"
value={search}
className="w-64"
onChange={(e) => setSearch(e.target.value)}
placeholder="Search for a type"
icon={<Search size={14} />}
/>
<div className="flex items-center justify-between">
<div className="flex items-center gap-x-2">
<SchemaSelector
className="w-[260px]"
size="small"
showError={false}
selectedSchemaName={selectedSchema}
onSelectSchema={setSelectedSchema}
/>
<Input
size="small"
value={search}
className="w-64"
onChange={(e) => setSearch(e.target.value)}
placeholder="Search for a type"
icon={<Search size={14} />}
/>
</div>
{!isLocked && (
<Button className="ml-auto" type="primary" onClick={() => setShowCreateTypePanel(true)}>
Create type
</Button>
)}
<div className="flex items-center gap-x-2">
<DocsButton href="https://www.postgresql.org/docs/current/datatype-enum.html" />
{!isLocked && (
<Button className="ml-auto" type="primary" onClick={() => setShowCreateTypePanel(true)}>
Create type
</Button>
)}
</div>
</div>
{isLocked && <ProtectedSchemaWarning schema={selectedSchema} entity="enumerated types" />}
@@ -1,17 +1,17 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { isNull, partition } from 'lodash'
import { AlertCircle, ExternalLink, Search } from 'lucide-react'
import Link from 'next/link'
import { AlertCircle, Search } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import InformationBox from 'components/ui/InformationBox'
import NoSearchResults from 'components/ui/NoSearchResults'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
import { Button, Input } from 'ui'
import { Input } from 'ui'
import ExtensionCard from './ExtensionCard'
import ExtensionCardSkeleton from './ExtensionCardSkeleton'
import { HIDDEN_EXTENSIONS, SEARCH_TERMS } from './Extensions.constants'
@@ -55,7 +55,7 @@ const Extensions = () => {
return (
<>
<div className="mb-4">
<div className="flex items-center gap-2 flex-wrap">
<div className="flex items-center justify-between">
<Input
size="small"
placeholder="Search for an extension"
@@ -64,27 +64,17 @@ const Extensions = () => {
className="w-64"
icon={<Search size={14} />}
/>
{isPermissionsLoaded && !canUpdateExtensions ? (
<div className="w-[500px]">
<InformationBox
icon={<AlertCircle className="text-foreground-light" size={18} strokeWidth={2} />}
title="You need additional permissions to update database extensions"
/>
</div>
) : (
<Button className="ml-auto" asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/database/extensions"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
)}
<DocsButton href="https://supabase.com/docs/guides/database/extensions" />
</div>
</div>
{isPermissionsLoaded && !canUpdateExtensions && (
<InformationBox
icon={<AlertCircle className="text-foreground-light" size={18} strokeWidth={2} />}
title="You need additional permissions to update database extensions"
/>
)}
{isLoading ? (
<div className="my-8 w-full space-y-12">
<div className="space-y-4">
@@ -1,20 +1,20 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { includes, map as lodashMap, uniqBy } from 'lodash'
import Link from 'next/link'
import { BookOpen, Search } from 'lucide-react'
import { useState } from 'react'
import { Button, Input } from 'ui'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import NoSearchResults from 'components/ui/NoSearchResults'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useDatabaseHooksQuery } from 'data/database-triggers/database-triggers-query'
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
import { noop } from 'lib/void'
import { ExternalLink, Search } from 'lucide-react'
import HooksListEmpty from './HooksListEmpty'
import SchemaTable from './SchemaTable'
import { DocsButton } from 'components/ui/DocsButton'
export interface HooksListProps {
createHook: () => void
@@ -55,43 +55,23 @@ const HooksList = ({ createHook = noop, editHook = noop, deleteHook = noop }: Ho
className="w-64"
onChange={(e) => setFilterString(e.target.value)}
/>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/database/webhooks"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>
<Button
disabled={!isPermissionsLoaded || !canCreateWebhooks}
onClick={() => createHook()}
>
Create a new hook
</Button>
</Tooltip.Trigger>
{isPermissionsLoaded && !canCreateWebhooks && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">
You need additional permissions to create webhooks
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
)}
</Tooltip.Root>
<div className="flex items-center gap-x-2">
<DocsButton href="https://supabase.com/docs/guides/database/webhooks" />
<ButtonTooltip
onClick={() => createHook()}
disabled={!isPermissionsLoaded || !canCreateWebhooks}
tooltip={{
content: {
side: 'bottom',
text:
isPermissionsLoaded && !canCreateWebhooks
? 'You need additional permissions to create webhooks'
: undefined,
},
}}
>
Create a new hook
</ButtonTooltip>
</div>
</div>
@@ -1,6 +1,7 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import { isEmpty } from 'lodash'
import { ArrowLeft, Edit, Trash } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
@@ -8,6 +9,7 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { FormActions } from 'components/ui/Forms/FormActions'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
@@ -16,7 +18,6 @@ import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-ex
import { invalidateSchemasQuery } from 'data/database/schemas-query'
import { useFDWCreateMutation } from 'data/fdw/fdw-create-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { ArrowLeft, Edit, ExternalLink, Trash } from 'lucide-react'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -25,6 +26,7 @@ import {
Form,
Input,
WarningIcon,
cn,
} from 'ui'
import InputField from './InputField'
import WrapperTableEditor from './WrapperTableEditor'
@@ -133,200 +135,192 @@ const CreateWrapper = () => {
}
return (
<FormsContainer>
<div>
<div className="relative flex items-center justify-between mb-6">
<div
className={[
'transition cursor-pointer',
'absolute -left-20 top-1 opacity-75 hover:opacity-100',
].join(' ')}
>
<Link href={`/project/${ref}/integrations/wrappers`}>
<div className="flex items-center space-x-2">
<ArrowLeft strokeWidth={1.5} size={14} />
<p className="text-sm">Back</p>
</div>
</Link>
</div>
<h3 className="mb-2 text-xl text-foreground">Create a {wrapperMeta.label} Wrapper</h3>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link href={wrapperMeta.docsUrl} target="_blank" rel="noreferrer">
Documentation
<FormsContainer className="[&>div]:space-y-0">
<div className="relative flex items-center justify-between mb-6">
<div
className={cn(
'transition cursor-pointer',
'absolute -left-20 top-1 opacity-75 hover:opacity-100'
)}
>
<Link href={`/project/${ref}/integrations/wrappers`}>
<div className="flex items-center space-x-2">
<ArrowLeft strokeWidth={1.5} size={14} />
<p className="text-sm">Back</p>
</div>
</Link>
</div>
<h3 className="mb-2 text-xl text-foreground">Create a {wrapperMeta.label} Wrapper</h3>
<DocsButton href={wrapperMeta.docsUrl} />
</div>
{hasRequiredVersion ? (
<Form id={formId} initialValues={initialValues} onSubmit={onSubmit}>
{({ handleReset, values, initialValues }: any) => {
const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
return (
<FormPanel
disabled={!canCreateWrapper}
footer={
<div className="flex px-8 py-4">
<FormActions
form={formId}
isSubmitting={isCreating}
hasChanges={hasChanges}
handleReset={handleReset}
disabled={!canCreateWrapper}
helper={
!canCreateWrapper
? 'You need additional permissions to create a foreign data wrapper'
: undefined
}
/>
</div>
}
>
<FormSection header={<FormSectionLabel>Wrapper Configuration</FormSectionLabel>}>
<FormSectionContent loading={false}>
<Input
id="wrapper_name"
label="Wrapper Name"
error={formErrors.wrapper_name}
descriptionText={
(values?.wrapper_name ?? '').length > 0 ? (
<>
Your wrapper's server name will be{' '}
<code className="text-xs">{values.wrapper_name}_server</code>
</>
) : (
''
)
}
/>
</FormSectionContent>
</FormSection>
<FormSection
header={<FormSectionLabel>{wrapperMeta.label} Configuration</FormSectionLabel>}
>
<FormSectionContent loading={false}>
{wrapperMeta.server.options
.filter((option) => !option.hidden)
.map((option) => (
<InputField
key={option.name}
option={option}
loading={false}
error={formErrors[option.name]}
/>
))}
</FormSectionContent>
</FormSection>
<FormSection
header={
<FormSectionLabel>
<p>Foreign Tables</p>
<p className="text-foreground-light mt-2 w-[90%]">
You can query your data from these foreign tables after the wrapper is
created
</p>
</FormSectionLabel>
}
>
<FormSectionContent loading={false}>
{newTables.length === 0 ? (
<div className="flex justify-end translate-y-4">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
) : (
<div className="space-y-2">
{newTables.map((table, i) => (
<div
key={`${table.schema_name}.${table.table_name}`}
className="flex items-center justify-between px-4 py-2 border rounded-md border-control"
>
<div>
<p className="text-sm">
{table.schema_name}.{table.table_name}
</p>
<p className="text-sm text-foreground-light">
Columns:{' '}
{table.columns.map((column: any) => column.name).join(', ')}
</p>
</div>
<div className="flex items-center space-x-2">
<Button
type="default"
className="px-1"
icon={<Edit />}
onClick={() => {
setIsEditingTable(true)
setSelectedTableToEdit({ ...table, tableIndex: i })
}}
/>
<Button
type="default"
className="px-1"
icon={<Trash />}
onClick={() => {
setNewTables((prev) => prev.filter((_, j) => j !== i))
}}
/>
</div>
</div>
))}
</div>
)}
{newTables.length > 0 && (
<div className="flex justify-end">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
)}
{newTables.length === 0 && formErrors.tables && (
<p className="text-sm text-right text-red-900">{formErrors.tables}</p>
)}
</FormSectionContent>
</FormSection>
</FormPanel>
)
}}
</Form>
) : (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>
Your extension version is outdated for this wrapper.
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="flex flex-col gap-y-2">
<p>
The {wrapperMeta.label} wrapper requires a minimum extension version of{' '}
{wrapperMeta.minimumExtensionVersion}. You have version{' '}
{wrappersExtension?.installed_version} installed. Please{' '}
{databaseNeedsUpgrading && 'upgrade your database then '}update the extension by
disabling and enabling the <code className="text-xs">wrappers</code> extension to
create this wrapper.
</p>
<p className="text-warning">
Warning: Before reinstalling the wrapper extension, you must first remove all existing
wrappers. Afterward, you can recreate the wrappers.
</p>
</AlertDescription_Shadcn_>
<AlertDescription_Shadcn_ className="mt-3">
<Button asChild type="default">
<Link
href={
databaseNeedsUpgrading
? `/project/${ref}/settings/infrastructure`
: `/project/${ref}/database/extensions?filter=wrappers`
}
>
{databaseNeedsUpgrading ? 'Upgrade database' : 'View wrappers extension'}
</Link>
</Button>
</div>
</div>
{hasRequiredVersion ? (
<Form id={formId} initialValues={initialValues} onSubmit={onSubmit}>
{({ handleReset, values, initialValues }: any) => {
const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
return (
<FormPanel
disabled={!canCreateWrapper}
footer={
<div className="flex px-8 py-4">
<FormActions
form={formId}
isSubmitting={isCreating}
hasChanges={hasChanges}
handleReset={handleReset}
disabled={!canCreateWrapper}
helper={
!canCreateWrapper
? 'You need additional permissions to create a foreign data wrapper'
: undefined
}
/>
</div>
}
>
<FormSection header={<FormSectionLabel>Wrapper Configuration</FormSectionLabel>}>
<FormSectionContent loading={false}>
<Input
id="wrapper_name"
label="Wrapper Name"
error={formErrors.wrapper_name}
descriptionText={
(values?.wrapper_name ?? '').length > 0 ? (
<>
Your wrapper's server name will be{' '}
<code className="text-xs">{values.wrapper_name}_server</code>
</>
) : (
''
)
}
/>
</FormSectionContent>
</FormSection>
<FormSection
header={<FormSectionLabel>{wrapperMeta.label} Configuration</FormSectionLabel>}
>
<FormSectionContent loading={false}>
{wrapperMeta.server.options
.filter((option) => !option.hidden)
.map((option) => (
<InputField
key={option.name}
option={option}
loading={false}
error={formErrors[option.name]}
/>
))}
</FormSectionContent>
</FormSection>
<FormSection
header={
<FormSectionLabel>
<p>Foreign Tables</p>
<p className="text-foreground-light mt-2 w-[90%]">
You can query your data from these foreign tables after the wrapper is
created
</p>
</FormSectionLabel>
}
>
<FormSectionContent loading={false}>
{newTables.length === 0 ? (
<div className="flex justify-end translate-y-4">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
) : (
<div className="space-y-2">
{newTables.map((table, i) => (
<div
key={`${table.schema_name}.${table.table_name}`}
className="flex items-center justify-between px-4 py-2 border rounded-md border-control"
>
<div>
<p className="text-sm">
{table.schema_name}.{table.table_name}
</p>
<p className="text-sm text-foreground-light">
Columns:{' '}
{table.columns.map((column: any) => column.name).join(', ')}
</p>
</div>
<div className="flex items-center space-x-2">
<Button
type="default"
className="px-1"
icon={<Edit />}
onClick={() => {
setIsEditingTable(true)
setSelectedTableToEdit({ ...table, tableIndex: i })
}}
/>
<Button
type="default"
className="px-1"
icon={<Trash />}
onClick={() => {
setNewTables((prev) => prev.filter((_, j) => j !== i))
}}
/>
</div>
</div>
))}
</div>
)}
{newTables.length > 0 && (
<div className="flex justify-end">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
)}
{newTables.length === 0 && formErrors.tables && (
<p className="text-sm text-right text-red-900">{formErrors.tables}</p>
)}
</FormSectionContent>
</FormSection>
</FormPanel>
)
}}
</Form>
) : (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>
Your extension version is outdated for this wrapper.
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="flex flex-col gap-y-2">
<p>
The {wrapperMeta.label} wrapper requires a minimum extension version of{' '}
{wrapperMeta.minimumExtensionVersion}. You have version{' '}
{wrappersExtension?.installed_version} installed. Please{' '}
{databaseNeedsUpgrading && 'upgrade your database then '}update the extension by
disabling and enabling the <code className="text-xs">wrappers</code> extension to
create this wrapper.
</p>
<p className="text-warning">
Warning: Before reinstalling the wrapper extension, you must first remove all
existing wrappers. Afterward, you can recreate the wrappers.
</p>
</AlertDescription_Shadcn_>
<AlertDescription_Shadcn_ className="mt-3">
<Button asChild type="default">
<Link
href={
databaseNeedsUpgrading
? `/project/${ref}/settings/infrastructure`
: `/project/${ref}/database/extensions?filter=wrappers`
}
>
{databaseNeedsUpgrading ? 'Upgrade database' : 'View wrappers extension'}
</Link>
</Button>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
<WrapperTableEditor
visible={isEditingTable}
@@ -1,6 +1,7 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import { isEmpty } from 'lodash'
import { ArrowLeft, Edit, Loader, Trash } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
@@ -8,6 +9,7 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { FormActions } from 'components/ui/Forms/FormActions'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
@@ -20,8 +22,7 @@ import { getDecryptedValue } from 'data/vault/vault-secret-decrypted-value-query
import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useImmutableValue } from 'hooks/misc/useImmutableValue'
import { ArrowLeft, Edit, ExternalLink, Loader, Trash } from 'lucide-react'
import { Button, Form, Input } from 'ui'
import { Button, cn, Form, Input } from 'ui'
import InputField from './InputField'
import WrapperTableEditor from './WrapperTableEditor'
import {
@@ -160,237 +161,222 @@ const EditWrapper = () => {
}
return (
<FormsContainer>
<div>
<div className="relative flex items-center justify-between mb-6">
<div
className={[
'transition cursor-pointer',
'absolute -left-20 top-1 opacity-75 hover:opacity-100',
].join(' ')}
>
<Link href={`/project/${ref}/integrations/wrappers`}>
<div className="flex items-center space-x-2">
<ArrowLeft strokeWidth={1.5} size={14} />
<p className="text-sm">Back</p>
</div>
</Link>
</div>
<h3 className="mb-2 text-xl text-foreground">
Edit {wrapperMeta.label} wrapper: {wrapper.name}
</h3>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.github.io/wrappers/stripe/"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
</div>
<FormsContainer className="[&>div]:space-y-0">
<div className="relative flex items-center justify-between mb-6">
<div
className={cn(
'transition cursor-pointer',
'absolute -left-20 top-1 opacity-75 hover:opacity-100'
)}
>
<Link href={`/project/${ref}/integrations/wrappers`}>
<div className="flex items-center space-x-2">
<ArrowLeft strokeWidth={1.5} size={14} />
<p className="text-sm">Back</p>
</div>
</Link>
</div>
<h3 className="mb-2 text-xl text-foreground">
Edit {wrapperMeta.label} wrapper: {wrapper.name}
</h3>
<DocsButton href="https://supabase.github.io/wrappers/stripe/" />
</div>
<Form id={formId} initialValues={initialValues} onSubmit={onSubmit}>
{({ handleReset, values, initialValues, resetForm }: any) => {
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
const [loadingSecrets, setLoadingSecrets] = useState(false)
<Form id={formId} initialValues={initialValues} onSubmit={onSubmit}>
{({ handleReset, values, initialValues, resetForm }: any) => {
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
const [loadingSecrets, setLoadingSecrets] = useState(false)
const initialTables = formatWrapperTables(wrapper?.tables ?? [])
const hasFormChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
const hasTableChanges = JSON.stringify(initialTables) !== JSON.stringify(wrapperTables)
const hasChanges = hasFormChanges || hasTableChanges
const initialTables = formatWrapperTables(wrapper?.tables ?? [])
const hasFormChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
const hasTableChanges = JSON.stringify(initialTables) !== JSON.stringify(wrapperTables)
const hasChanges = hasFormChanges || hasTableChanges
const encryptedOptions = wrapperMeta.server.options.filter((option) => option.encrypted)
const encryptedOptions = wrapperMeta.server.options.filter((option) => option.encrypted)
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
const fetchEncryptedValues = async () => {
setLoadingSecrets(true)
// If the secrets haven't loaded, escape and run the effect again when they're loaded
if (isSecretsLoading) {
return
}
const res = await Promise.all(
encryptedOptions.map(async (option) => {
const secret = secrets?.find(
(secret) => secret.name === `${wrapper.name}_${option.name}`
)
if (secret !== undefined) {
const value = await getDecryptedValue({
projectRef: project?.ref,
connectionString: project?.connectionString,
id: secret.id,
})
return { [option.name]: value[0]?.decrypted_secret ?? '' }
} else {
return { [option.name]: '' }
}
})
)
const secretValues = res.reduce((a: any, b: any) => {
const [key] = Object.keys(b)
return { ...a, [key]: b[key] }
}, {})
resetForm({
values: { ...values, ...secretValues },
initialValues: { ...initialValues, ...secretValues },
})
setLoadingSecrets(false)
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
const fetchEncryptedValues = async () => {
setLoadingSecrets(true)
// If the secrets haven't loaded, escape and run the effect again when they're loaded
if (isSecretsLoading) {
return
}
if (encryptedOptions.length > 0) fetchEncryptedValues()
}, [isSecretsLoading])
const res = await Promise.all(
encryptedOptions.map(async (option) => {
const secret = secrets?.find(
(secret) => secret.name === `${wrapper.name}_${option.name}`
)
if (secret !== undefined) {
const value = await getDecryptedValue({
projectRef: project?.ref,
connectionString: project?.connectionString,
id: secret.id,
})
return { [option.name]: value[0]?.decrypted_secret ?? '' }
} else {
return { [option.name]: '' }
}
})
)
const secretValues = res.reduce((a: any, b: any) => {
const [key] = Object.keys(b)
return { ...a, [key]: b[key] }
}, {})
return (
<FormPanel
disabled={!canUpdateWrapper}
footer={
<div className="flex px-8 py-4">
<FormActions
form={formId}
isSubmitting={isSaving}
hasChanges={hasChanges}
handleReset={() => {
handleReset()
setWrapperTables(initialTables)
}}
disabled={!canUpdateWrapper}
helper={
!canUpdateWrapper
? 'You need additional permissions to edit a foreign data wrapper'
: undefined
}
/>
</div>
resetForm({
values: { ...values, ...secretValues },
initialValues: { ...initialValues, ...secretValues },
})
setLoadingSecrets(false)
}
if (encryptedOptions.length > 0) fetchEncryptedValues()
}, [isSecretsLoading])
return (
<FormPanel
disabled={!canUpdateWrapper}
footer={
<div className="flex px-8 py-4">
<FormActions
form={formId}
isSubmitting={isSaving}
hasChanges={hasChanges}
handleReset={() => {
handleReset()
setWrapperTables(initialTables)
}}
disabled={!canUpdateWrapper}
helper={
!canUpdateWrapper
? 'You need additional permissions to edit a foreign data wrapper'
: undefined
}
/>
</div>
}
>
<FormSection header={<FormSectionLabel>Wrapper Configuration</FormSectionLabel>}>
<FormSectionContent loading={false}>
<Input
id="wrapper_name"
label="Wrapper Name"
error={formErrors.wrapper_name}
descriptionText={
values.wrapper_name !== initialValues.wrapper_name ? (
<>
Your wrapper's server name will be updated to{' '}
<code className="text-xs">{values.wrapper_name}_server</code>
</>
) : (
<>
Your wrapper's server name is{' '}
<code className="text-xs">{values.wrapper_name}_server</code>
</>
)
}
/>
</FormSectionContent>
</FormSection>
<FormSection
header={<FormSectionLabel>{wrapperMeta.label} Configuration</FormSectionLabel>}
>
<FormSectionContent loading={false}>
{wrapperMeta.server.options
.filter((option) => !option.hidden)
.map((option) => (
<InputField
key={option.name}
option={option}
loading={option.encrypted ? loadingSecrets : false}
error={formErrors[option.name]}
/>
))}
</FormSectionContent>
</FormSection>
<FormSection
header={
<FormSectionLabel>
<p>Foreign Tables</p>
<p className="text-foreground-light mt-2 w-[90%]">
You can query your data from these foreign tables after the wrapper is created
</p>
</FormSectionLabel>
}
>
<FormSection header={<FormSectionLabel>Wrapper Configuration</FormSectionLabel>}>
<FormSectionContent loading={false}>
<Input
id="wrapper_name"
label="Wrapper Name"
error={formErrors.wrapper_name}
descriptionText={
values.wrapper_name !== initialValues.wrapper_name ? (
<>
Your wrapper's server name will be updated to{' '}
<code className="text-xs">{values.wrapper_name}_server</code>
</>
) : (
<>
Your wrapper's server name is{' '}
<code className="text-xs">{values.wrapper_name}_server</code>
</>
)
}
/>
</FormSectionContent>
</FormSection>
<FormSection
header={<FormSectionLabel>{wrapperMeta.label} Configuration</FormSectionLabel>}
>
<FormSectionContent loading={false}>
{wrapperMeta.server.options
.filter((option) => !option.hidden)
.map((option) => (
<InputField
key={option.name}
option={option}
loading={option.encrypted ? loadingSecrets : false}
error={formErrors[option.name]}
/>
))}
</FormSectionContent>
</FormSection>
<FormSection
header={
<FormSectionLabel>
<p>Foreign Tables</p>
<p className="text-foreground-light mt-2 w-[90%]">
You can query your data from these foreign tables after the wrapper is
created
</p>
</FormSectionLabel>
}
>
<FormSectionContent loading={false}>
{wrapperTables.length === 0 ? (
<div className="flex justify-end translate-y-4">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
) : (
<div className="space-y-2">
{wrapperTables.map((table, i) => {
const target = table?.table ?? table.object
<FormSectionContent loading={false}>
{wrapperTables.length === 0 ? (
<div className="flex justify-end translate-y-4">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
) : (
<div className="space-y-2">
{wrapperTables.map((table, i) => {
const target = table?.table ?? table.object
return (
<div
key={`${table.schema_name}.${table.table_name}`}
className="flex items-center justify-between px-4 py-2 border rounded-md border-control"
>
<div>
<p className="text-sm">
{table.schema_name}.{table.table_name}{' '}
</p>
<p className="text-sm text-foreground-light mt-1">
Target: {target}
</p>
<p className="text-sm text-foreground-light">
Columns:{' '}
{table.columns.map((column: any) => column.name).join(', ')}
</p>
</div>
<div className="flex items-center space-x-2">
<Button
type="default"
className="px-1"
icon={<Edit />}
onClick={() => {
setIsEditingTable(true)
setSelectedTableToEdit({ ...table, tableIndex: i })
}}
/>
<Button
type="default"
className="px-1"
icon={<Trash />}
onClick={() => {
setWrapperTables((prev) => prev.filter((_, j) => j !== i))
}}
/>
</div>
return (
<div
key={`${table.schema_name}.${table.table_name}`}
className="flex items-center justify-between px-4 py-2 border rounded-md border-control"
>
<div>
<p className="text-sm">
{table.schema_name}.{table.table_name}{' '}
</p>
<p className="text-sm text-foreground-light mt-1">Target: {target}</p>
<p className="text-sm text-foreground-light">
Columns:{' '}
{table.columns.map((column: any) => column.name).join(', ')}
</p>
</div>
)
})}
</div>
)}
{wrapperTables.length > 0 && (
<div className="flex justify-end">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
)}
{wrapperTables.length === 0 && formErrors.tables && (
<p className="text-sm text-right text-red-900">{formErrors.tables}</p>
)}
</FormSectionContent>
</FormSection>
</FormPanel>
)
}}
</Form>
</div>
<div className="flex items-center space-x-2">
<Button
type="default"
className="px-1"
icon={<Edit />}
onClick={() => {
setIsEditingTable(true)
setSelectedTableToEdit({ ...table, tableIndex: i })
}}
/>
<Button
type="default"
className="px-1"
icon={<Trash />}
onClick={() => {
setWrapperTables((prev) => prev.filter((_, j) => j !== i))
}}
/>
</div>
</div>
)
})}
</div>
)}
{wrapperTables.length > 0 && (
<div className="flex justify-end">
<Button type="default" onClick={() => setIsEditingTable(true)}>
Add foreign table
</Button>
</div>
)}
{wrapperTables.length === 0 && formErrors.tables && (
<p className="text-sm text-right text-red-900">{formErrors.tables}</p>
)}
</FormSectionContent>
</FormSection>
</FormPanel>
)
}}
</Form>
<WrapperTableEditor
visible={isEditingTable}
@@ -1,7 +1,7 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { AnimatePresence, motion } from 'framer-motion'
import { ExternalLink, HelpCircle, InfoIcon, RotateCcw } from 'lucide-react'
import { HelpCircle, InfoIcon, RotateCcw } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
@@ -10,6 +10,7 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import DiskSpaceBar from 'components/interfaces/DiskManagement/DiskSpaceBar'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import {
useDiskAttributesQuery,
@@ -43,8 +44,10 @@ import {
TooltipContent_Shadcn_,
TooltipTrigger_Shadcn_,
} from 'ui'
import { Admonition } from 'ui-patterns'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { FormFooterChangeBadge } from '../DataWarehouse/FormFooterChangeBadge'
import { Markdown } from '../Markdown'
import BillingChangeBadge from './BillingChangeBadge'
import { DiskCountdownRadial } from './DiskCountdownRadial'
import {
@@ -60,7 +63,7 @@ import {
calculateIOPSPrice,
calculateThroughputPrice,
} from './DiskManagement.utils'
import { getDiskStorageSchema, DiskStorageSchemaType } from './DiskManagementPanelSchema'
import { DiskStorageSchemaType, getDiskStorageSchema } from './DiskManagementPanelSchema'
import { DiskManagementPlanUpgradeRequired } from './DiskManagementPlanUpgradeRequired'
import {
DiskManagementDiskSizeReadReplicas,
@@ -68,8 +71,6 @@ import {
DiskManagementThroughputReadReplicas,
} from './DiskManagementReadReplicas'
import { DiskManagementReviewAndSubmitDialog } from './DiskManagementReviewAndSubmitDialog'
import { Admonition } from 'ui-patterns'
import { Markdown } from '../Markdown'
export function DiskManagementPanelForm() {
const { project } = useProjectContext()
@@ -634,13 +635,10 @@ export function DiskManagementPanelForm() {
/>
}
>
<div className="flex items-center gap-x-2">
<Button asChild type="default" icon={<ExternalLink />}>
<Link href="https://supabase.com/docs/guides/platform/database-size#reducing-disk-size">
Documentation
</Link>
</Button>
</div>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/platform/database-size#reducing-disk-size"
/>
</Admonition>
)}
</>
@@ -1,12 +1,14 @@
import { Download } from 'lucide-react'
import Link from 'next/link'
import { useState } from 'react'
import { toast } from 'sonner'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useParams } from 'common'
import CodeSnippet from 'components/interfaces/Docs/CodeSnippet'
import { DocsButton } from 'components/ui/DocsButton'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { generateTypes } from 'data/projects/project-type-generation-query'
import { Button } from 'ui'
import { ExternalLink, Download } from 'lucide-react'
interface Props {
selectedLang: 'bash' | 'js'
@@ -41,15 +43,7 @@ export default function GeneratingTypes({ selectedLang }: Props) {
<>
<h2 className="doc-heading flex items-center justify-between">
<span>Generating types</span>
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/database/api/generating-types"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/api/generating-types" />
</h2>
<div className="doc-section">
<article className="code-column text-foreground">
@@ -1,22 +1,23 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { ExternalLink, Search } from 'lucide-react'
import { Search } from 'lucide-react'
import { useState } from 'react'
import { toast } from 'sonner'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import NoPermission from 'components/ui/NoPermission'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useSecretsDeleteMutation } from 'data/secrets/secrets-delete-mutation'
import { ProjectSecret, useSecretsQuery } from 'data/secrets/secrets-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Badge, Button } from 'ui'
import { Badge } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import AddNewSecretModal from './AddNewSecretModal'
import EdgeFunctionSecret from './EdgeFunctionSecret'
import { Input } from 'ui-patterns/DataInputs/Input'
const EdgeFunctionSecrets = () => {
const { ref: projectRef } = useParams()
@@ -66,15 +67,7 @@ const EdgeFunctionSecrets = () => {
icon={<Search size={14} />}
/>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/functions/secrets"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/functions/secrets" />
<ButtonTooltip
disabled={!canUpdateSecrets}
onClick={() => setShowCreateSecret(true)}
@@ -1,6 +1,6 @@
import { Code, ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { ExternalLink } from 'lucide-react'
import { DocsButton } from 'components/ui/DocsButton'
import { Button } from 'ui'
import TerminalInstructions from './TerminalInstructions'
@@ -17,24 +17,16 @@ const FunctionsEmptyState = () => {
close to your users. They can be used for listening to webhooks or integrating your
Supabase project with third-parties.
</p>
<div className="flex gap-2">
<div className="flex gap-x-2">
<DocsButton href="https://supabase.com/docs/guides/functions" />
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/functions"
<a
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<Button asChild type="default" icon={<Code />}>
<Link
href="https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions"
target="_blank"
rel="noreferrer"
>
Examples
</Link>
</a>
</Button>
</div>
</div>
@@ -1,9 +1,10 @@
import { Code, ExternalLink, Maximize2, Minimize2, Terminal } from 'lucide-react'
import { ExternalLink, Maximize2, Minimize2, Terminal } from 'lucide-react'
import { useRouter } from 'next/router'
import { ComponentPropsWithoutRef, ElementRef, forwardRef, useState } from 'react'
import { useParams } from 'common'
import CommandRender from 'components/interfaces/Functions/CommandRender'
import { DocsButton } from 'components/ui/DocsButton'
import { useAccessTokensQuery } from 'data/access-tokens/access-tokens-query'
import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
@@ -136,20 +137,12 @@ const TerminalInstructions = forwardRef<
</p>
</div>
<div className="flex gap-2">
<DocsButton href="https://supabase.com/docs/guides/functions" />
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/functions"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
<Button asChild type="default" icon={<Code />}>
<a
href="https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions"
target="_blank"
rel="noreferrer"
>
Examples
</a>
@@ -1,16 +1,13 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useState } from 'react'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Button } from 'ui'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
export const CronJobsDisabledState = () => {
const [showEnableExtensionModal, setShowEnableExtensionModal] = useState(false)
@@ -42,39 +39,22 @@ export const CronJobsDisabledState = () => {
</div>
<div className="flex items-center gap-3 mt-4">
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>
<Button type="primary" onClick={() => setShowEnableExtensionModal(true)}>
Enable Cron Jobs
</Button>
</Tooltip.Trigger>
{!canToggleExtensions && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">
You need additional permissions to enable pg_cron for this project
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
)}
</Tooltip.Root>
<Button asChild type="text" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/database/extensions/pg_cron"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<ButtonTooltip
type="primary"
disabled={!canToggleExtensions}
onClick={() => setShowEnableExtensionModal(true)}
tooltip={{
content: {
side: 'bottom',
text: !canToggleExtensions
? 'You need additional permissions to enable pg_cron for this project'
: undefined,
},
}}
>
Enable Cron Jobs
</ButtonTooltip>
<DocsButton href="https://supabase.com/docs/guides/database/extensions/pg_cron" />
</div>
</ProductEmptyState>
</div>
@@ -1,9 +1,6 @@
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import AlertError from 'components/ui/AlertError'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
@@ -11,11 +8,12 @@ import { CronJob, useCronJobsQuery } from 'data/database-cron-jobs/database-cron
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { Button, Sheet, SheetContent } from 'ui'
import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
import { DocsButton } from 'components/ui/DocsButton'
import { CreateCronJobSheet } from './CreateCronJobSheet'
import { CronJobCard } from './CronJobCard'
import { CronJobsDisabledState } from './CronJobsDisabledState'
import DeleteCronJob from './DeleteCronJob'
import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
export const CronJobsListing = () => {
const { project } = useProjectContext()
@@ -69,15 +67,7 @@ export const CronJobsListing = () => {
description="Schedule and automate tasks like running queries or maintenance routines at specified intervals"
actions={
<>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/database/extensions/pg_cron"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/extensions/pg_cron" />
<Button
type="primary"
onClick={() =>
@@ -1,6 +1,5 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { ExternalLink, TrashIcon } from 'lucide-react'
import Link from 'next/link'
import { ReactNode, useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
@@ -8,6 +7,7 @@ import { z } from 'zod'
import { useParams } from 'common'
import { LogDrainData, useLogDrainsQuery } from 'data/log-drains/log-drains-query'
import { useFlag } from 'hooks/ui/useFlag'
import {
Button,
Form_Shadcn_,
@@ -36,9 +36,9 @@ import {
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { InfoTooltip } from 'ui-patterns/info-tooltip'
import { DATADOG_REGIONS, LOG_DRAIN_TYPES, LogDrainType } from './LogDrains.constants'
import { urlRegex } from '../Auth/Auth.constants'
import { useFlag } from 'hooks/ui/useFlag'
import { DATADOG_REGIONS, LOG_DRAIN_TYPES, LogDrainType } from './LogDrains.constants'
import { DocsButton } from 'components/ui/DocsButton'
const FORM_ID = 'log-drain-destination-form'
@@ -510,18 +510,13 @@ export function LogDrainDestinationSheetForm({
isReactForm={false}
layout="horizontal"
label={
<div className="text-foreground-light">
<div className="flex flex-col gap-y-2 text-foreground-light">
Additional drain cost
<div className="text-foreground-lighter mt-2">
<Link
target="_blank"
rel="noopener noreferrer"
className="underline flex gap-1 items-center"
href="https://supabase.com/docs/guides/platform/log-drains"
>
Documentation <ExternalLink className="w-4 h-4" />
</Link>
</div>
<DocsButton
abbrev={false}
className="w-min"
href="https://supabase.com/docs/guides/platform/log-drains"
/>
</div>
}
>
@@ -1,10 +1,10 @@
import type { OAuthScope } from '@supabase/shared-types/out/constants'
import Link from 'next/link'
import { Edit, Upload } from 'lucide-react'
import { ChangeEvent, useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { Edit, ExternalLink, Upload } from 'lucide-react'
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import {
OAuthAppCreateResponse,
useOAuthAppCreateMutation,
@@ -336,15 +336,7 @@ const PublishAppSidePanel = ({
of its projects.
</span>
</div>
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/platform/oauth-apps/oauth-scopes"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/oauth-apps/oauth-scopes" />
</div>
<ScopesPanel scopes={scopes} setScopes={setScopes} />
@@ -1,8 +1,9 @@
import { isEqual } from 'lodash'
import { ExternalLink, X } from 'lucide-react'
import { X } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import { useOrganizationRolesV2Query } from 'data/organization-members/organization-roles-query'
import { OrganizationMember } from 'data/organizations/organization-members-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
@@ -170,15 +171,7 @@ export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelP
<p className="truncate" title={`Manage access for ${member.username}`}>
Manage access for {member.username}
</p>
<Button asChild type="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/platform/access-control"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/access-control" />
</SheetHeader>
<SheetSection className="h-full overflow-auto flex flex-col gap-y-4">
@@ -1,232 +0,0 @@
import { ExternalLink } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { OrganizationMember } from 'data/organizations/organization-members-query'
import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { Role } from 'types'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
WarningIcon,
cn,
} from 'ui'
import { MultiSelectV2 } from 'ui-patterns/MultiSelectDeprecated/MultiSelectV2'
interface UpdateRolesPanelProps {
visible: boolean
member: OrganizationMember
onClose: () => void
}
// [Joshen] This is just scaffolding the UI logic, will need to change quite a bit to fit the API structure
// [Joshen] Keeping this for now, but once everything is ready we can deprecate this file, no one is using
export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelProps) => {
const { slug } = useParams()
const organization = useSelectedOrganization()
const { data } = useOrganizationRolesQuery({ slug })
const { data: projects } = useProjectsQuery()
const [roleConfiguration, setRoleConfiguration] = useState<
{ id: number; name: string; projects: string[] }[]
>([])
// [Joshen] If any roles are applied to ALL projects, or there are overlapping projects across roles
const hasMoreThanOneRoleForAllProjects =
roleConfiguration.some((r) => r.projects.length === 0) && roleConfiguration.length > 1
const selectedProjectsAcrossRoles = roleConfiguration.reduce((obj, item) => {
return obj.concat(item.projects)
}, [] as string[])
const hasOverlappingProjects =
new Set(selectedProjectsAcrossRoles).size !== selectedProjectsAcrossRoles.length
const canSaveRoles =
roleConfiguration.length > 0 && !hasOverlappingProjects && !hasMoreThanOneRoleForAllProjects
const availableRoles = data?.roles ?? []
const appliedRoles = member.role_ids
.map((id) => {
return availableRoles.find((y) => id === y.id)
})
.filter((x) => x !== undefined) as Role[]
const sortByObject: any = ['Owner', 'Administrator', 'Developer'].reduce((obj, item, index) => {
return { ...obj, [item]: index }
}, {})
const onAddRole = (role: Role) => {
const updatedConfiguration = roleConfiguration.concat({ ...role, projects: [] })
setRoleConfiguration(
updatedConfiguration.sort((a, b) => sortByObject[a.name] - sortByObject[b.name])
)
}
const onRemoveRole = (role: Role) => {
setRoleConfiguration(roleConfiguration.filter((r) => r.id !== role.id))
}
const onUpdateRole = (role: Role, projects: string[]) => {
const updatedConfiguration = roleConfiguration.map((r) => {
if (role.id === r.id) {
return { ...r, projects }
} else {
return r
}
})
setRoleConfiguration(updatedConfiguration)
}
useEffect(() => {
// [Joshen] This is an assumption for the structure
if (visible) {
setRoleConfiguration(
appliedRoles.map((role) => {
return { ...role, projects: [] }
})
)
}
}, [visible])
return (
<Sheet open={visible} onOpenChange={() => onClose()}>
<SheetContent
showClose={false}
size="default"
className={cn('bg-surface-200 p-0 flex flex-row gap-0 !min-w-[400px]')}
>
<div className={cn('flex flex-col grow w-full')}>
<SheetHeader className={cn('py-3 flex flex-row justify-between items-center border-b')}>
<p>Manage access for team member</p>
<Button asChild type="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/platform/access-control"
>
Documentation
</a>
</Button>
</SheetHeader>
<div className="h-full flex flex-col gap-y-4 px-5 py-5">
<p className="text-sm">
Manage access for {member.username} to each project under {organization?.name}
</p>
{(hasOverlappingProjects || hasMoreThanOneRoleForAllProjects) && (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>
Members cannot be assigned multiple roles on a project
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Ensure that the member has at most one role assigned for each project
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
{roleConfiguration.length === 0 && (
<Alert_Shadcn_>
<WarningIcon />
<AlertTitle_Shadcn_>
Team members need to be assigned at least one role
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You may not remove all roles from a team member
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
{roleConfiguration.map((role) => {
const selectedProjects =
roleConfiguration.find((r) => r.id === role.id)?.projects ?? []
return (
<div
key={role.name}
className="flex flex-col border border-control rounded gap-y-2 px-3 py-3 bg-surface-200"
>
<div className="flex items-center justify-between">
<p className="text-sm">{role.name}</p>
<div className="flex items-center gap-x-2">
{role.projects.length > 0 && (
<Button type="default" onClick={() => onUpdateRole(role, [])}>
Apply to all projects
</Button>
)}
<Button type="default" onClick={() => onRemoveRole(role)}>
Remove role
</Button>
</div>
</div>
<MultiSelectV2
options={(projects ?? []).map((project) => {
return {
id: project.id,
name: project.name,
value: project.ref,
disabled: false,
}
})}
value={selectedProjects}
placeholder="All projects"
searchPlaceholder="Search for a project"
onChange={(projects) => onUpdateRole(role, projects)}
/>
</div>
)
})}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button type="default" className="w-min">
Assign a new role
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="start">
{availableRoles
.sort((a, b) => sortByObject[a.name] - sortByObject[b.name])
.map((role) => {
const alreadyAssigned = roleConfiguration.map((x) => x.id).includes(role.id)
return (
<DropdownMenuItem
key={role.id}
disabled={alreadyAssigned}
className="flex items-center justify-between"
onClick={() => onAddRole(role)}
>
<span className="text-foreground">{role.name}</span>
{alreadyAssigned && (
<span className="text-foreground-light">Already assigned</span>
)}
</DropdownMenuItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<SheetFooter className="flex items-center !justify-end px-5 py-4 w-full border-t">
<Button type="default" disabled={false} onClick={() => onClose()}>
Cancel
</Button>
<Button loading={false} disabled={!canSaveRoles} onClick={() => {}}>
Save roles
</Button>
</SheetFooter>
</div>
</SheetContent>
</Sheet>
)
}
@@ -1,15 +1,13 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import clsx from 'clsx'
import dayjs from 'dayjs'
import { ExternalLink, Info } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useMemo, useState } from 'react'
import { useParams } from 'common'
import { ScaffoldContainer, ScaffoldContainerLegacy } from 'components/layouts/Scaffold'
import DateRangePicker from 'components/to-be-cleaned/DateRangePicker'
import AlertError from 'components/ui/AlertError'
import InformationBox from 'components/ui/InformationBox'
import { DocsButton } from 'components/ui/DocsButton'
import NoPermission from 'components/ui/NoPermission'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useProjectsQuery } from 'data/projects/projects-query'
@@ -17,7 +15,8 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants/metrics'
import { Button, Listbox } from 'ui'
import { Listbox } from 'ui'
import { Admonition } from 'ui-patterns'
import { Restriction } from '../BillingSettings/Restriction'
import Activity from './Activity'
import Bandwidth from './Bandwidth'
@@ -183,35 +182,21 @@ const Usage = () => {
{selectedProjectRef ? (
<ScaffoldContainer className="mt-5">
<InformationBox
<Admonition
type="default"
title="Usage filtered by project"
description={
<div className="space-y-3">
<p>
You are currently viewing usage for the "
{selectedProject?.name || selectedProjectRef}" project. Since your organization is
description={`You are currently viewing usage for the "
${selectedProject?.name || selectedProjectRef}" project. Since your organization is
using the new organization-based billing, the included quota is for your whole
organization and not just this project. For billing purposes, we sum up usage from
all your projects. To view your usage quota, set the project filter above back to
"All Projects".
</p>
<div>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/platform/org-based-billing"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
</div>
</div>
}
defaultVisibility
hideCollapse
icon={<Info />}
/>
"All Projects".`}
>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/platform/org-based-billing"
/>
</Admonition>
</ScaffoldContainer>
) : (
<ScaffoldContainer id="restriction" className="mt-5">
@@ -1,15 +1,15 @@
import Link from 'next/link'
import { useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { generateTypes } from 'data/projects/project-type-generation-query'
import { Download } from 'lucide-react'
import { Button } from 'ui'
import ContentSnippet from '../ContentSnippet'
import { DOCS_CONTENT } from '../ProjectAPIDocs.constants'
import type { ContentProps } from './Content.types'
import { ExternalLink, Download } from 'lucide-react'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
const Entities = ({ language }: ContentProps) => {
const { ref } = useParams()
@@ -41,16 +41,8 @@ const Entities = ({ language }: ContentProps) => {
<ContentSnippet selectedLanguage={language} snippet={DOCS_CONTENT.entitiesIntroduction} />
<div>
<ContentSnippet selectedLanguage={language} snippet={DOCS_CONTENT.generatingTypes} />
<div className="flex items-center space-x-2 px-4 mt-3">
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/database/api/generating-types"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<div className="flex items-center gap-x-2 px-4 mt-3">
<DocsButton href="https://supabase.com/docs/guides/database/api/generating-types" />
<Button
type="default"
disabled={isGeneratingTypes}
@@ -1,10 +1,7 @@
import { useParams } from 'common'
import Link from 'next/link'
import { Button } from 'ui'
import { SimpleCodeBlock } from '@ui/components/SimpleCodeBlock'
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import { Markdown } from '../Markdown'
import { ExternalLink } from 'lucide-react'
interface ResourceContentProps {
selectedLanguage: 'js' | 'bash'
@@ -25,13 +22,7 @@ const ResourceContent = ({ selectedLanguage, snippet, codeSnippets }: ResourceCo
<div className="px-4 space-y-2">
<div className="flex items-center justify-between">
<h2 className="doc-heading">{snippet.title}</h2>
{snippet.docsUrl !== undefined && (
<Button asChild type="default" icon={<ExternalLink />}>
<Link href={snippet.docsUrl} target="_blank" rel="noreferrer">
Documentation
</Link>
</Button>
)}
{snippet.docsUrl !== undefined && <DocsButton abbrev={false} href={snippet.docsUrl} />}
</div>
{snippet.description !== undefined && (
<div className="doc-section">
@@ -23,6 +23,7 @@ import { useIsAPIDocsSidePanelEnabled } from '../App/FeaturePreview/FeaturePrevi
import { navigateToSection } from './Content/Content.utils'
import { DOCS_RESOURCE_CONTENT } from './ProjectAPIDocs.constants'
import { ChevronLeft, Code, ExternalLink } from 'lucide-react'
import { DocsButton } from 'components/ui/DocsButton'
const SecondLevelNav = () => {
const { ref } = useParams()
@@ -155,17 +156,7 @@ const SecondLevelNav = () => {
<p className="text-xs !leading-normal">
Head over to our docs site for the full API documentation.
</p>
<Button
asChild
className="!mt-2"
size="tiny"
type="default"
icon={<ExternalLink strokeWidth={1.5} />}
>
<Link href={content[section].docsUrl} target="_blank" rel="noreferrer">
Documentation
</Link>
</Button>
<DocsButton className="!mt-2" href={content[section].docsUrl} />
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
</div>
@@ -1,9 +1,9 @@
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { useDatabaseExtensionEnableMutation } from 'data/database-extensions/database-extension-enable-mutation'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button } from 'ui'
@@ -88,15 +88,7 @@ export const IndexAdvisorDisabledState = () => {
Enable extensions
</Button>
)}
<Button asChild type="outline" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/extensions/index_advisor"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/extensions/index_advisor" />
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
@@ -20,6 +20,7 @@ import {
Switch,
} from 'ui'
import { RealtimeConfig } from '../useRealtimeMessages'
import { DocsButton } from 'components/ui/DocsButton'
interface ChooseChannelPopoverProps {
config: RealtimeConfig
@@ -154,15 +155,11 @@ export const ChooseChannelPopover = ({ config, onChangeConfig }: ChooseChannelPo
)}
/>
<Button asChild type="default" className="w-min" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/realtime/authorization"
>
Documentation
</a>
</Button>
<DocsButton
abbrev={false}
className="w-min"
href="https://supabase.com/docs/guides/realtime/authorization"
/>
</form>
</Form_Shadcn_>
</>
@@ -12,6 +12,7 @@ import MessageSelection from './MessageSelection'
import type { LogData } from './Messages.types'
import NoChannelEmptyState from './NoChannelEmptyState'
import { ColumnRenderer } from './RealtimeMessageColumnRenderer'
import { DocsButton } from 'components/ui/DocsButton'
export const isErrorLog = (l: LogData) => {
return l.message === 'SYSTEM' && l.metadata?.status === 'error'
@@ -83,15 +84,7 @@ const NoResultAlert = ({
<p className="text-foreground">Not sure what to do?</p>
<p className="text-foreground-lighter text-xs">Browse our documentation</p>
</div>
<Button type="default" iconRight={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/realtime"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/realtime" />
</div>
</div>
</>
@@ -1,3 +1,4 @@
import { DocsButton } from 'components/ui/DocsButton'
import { ExternalLink } from 'lucide-react'
import { Button } from 'ui'
@@ -18,11 +19,7 @@ const NoChannelEmptyState = () => {
<p className="text-foreground">Not sure what to do?</p>
<p className="text-foreground-lighter text-xs">Browse our documentation</p>
</div>
<Button type="default" iconRight={<ExternalLink />}>
<a href="https://supabase.com/docs/guides/realtime" target="_blank" rel="noreferrer">
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/realtime" />
</div>
</div>
)
@@ -1,7 +1,8 @@
import { Check, ChevronDown, ExternalLink } from 'lucide-react'
import { Check, ChevronDown } from 'lucide-react'
import { toast } from 'sonner'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import InformationBox from 'components/ui/InformationBox'
import { useCreateAndExposeAPISchemaMutation } from 'data/api-settings/create-and-expose-api-schema-mutation'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
@@ -12,7 +13,6 @@ import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
CodeBlock,
CollapsibleContent_Shadcn_,
CollapsibleTrigger_Shadcn_,
@@ -119,15 +119,11 @@ export const HardenAPIModal = ({ visible, onClose }: HardenAPIModalProps) => {
<code className="text-xs text-foreground">public</code> schema to{' '}
<span className="text-brand">prevent accidental exposure of data</span>.
</p>
<Button asChild type="default" icon={<ExternalLink />} className="w-min mt-4">
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/hardening-data-api"
>
Documentation
</a>
</Button>
<DocsButton
abbrev={false}
className="w-min mt-4"
href="https://supabase.com/docs/guides/database/hardening-data-api"
/>
</DialogSection>
<DialogSectionSeparator />
@@ -1,7 +1,7 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { indexOf } from 'lodash'
import { ExternalLink, Lock } from 'lucide-react'
import { Lock } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
@@ -10,6 +10,7 @@ import { z } from 'zod'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { FormActions } from 'components/ui/Forms/FormActions'
import {
FormPanelContainer,
@@ -44,7 +45,6 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import {
MultiSelector,
MultiSelectorContent,
MultiSelectorInput,
MultiSelectorItem,
MultiSelectorList,
MultiSelectorTrigger,
@@ -196,15 +196,7 @@ export const PostgrestConfig = () => {
<FormPanelHeader className="flex items-center justify-between">
<span>Data API Settings</span>
<div className="flex items-center gap-x-2">
<Button asChild type="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/connecting-to-postgres#data-apis"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/connecting-to-postgres#data-apis" />
<Button type="default" icon={<Lock />} onClick={() => setShowModal(true)}>
Harden Data API
</Button>
@@ -14,6 +14,7 @@ import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { BASE_PATH } from 'lib/constants'
import { Badge, Button } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { DocsButton } from 'components/ui/DocsButton'
const BannedIPs = () => {
const { ref } = useParams()
@@ -69,18 +70,13 @@ const BannedIPs = () => {
return (
<div id="banned-ips">
<div className="flex items-center justify-between">
<div className="flex items-center justify-between mb-6">
<FormHeader
className="mb-0"
title="Network Bans"
description="List of IP addresses that are temporarily blocked if their traffic pattern looks abusive"
/>
<div className="flex items-center space-x-2 mb-6">
<Button asChild type="default" icon={<ExternalLink />}>
<a target="_blank" href="https://supabase.com/docs/reference/cli/supabase-network-bans">
Documentation
</a>
</Button>
</div>
<DocsButton href="https://supabase.com/docs/reference/cli/supabase-network-bans" />
</div>
<FormPanel>
{ipList && ipList.banned_ipv4_addresses.length > 0 ? (
@@ -2,7 +2,6 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { capitalize } from 'lodash'
import { ExternalLink } from 'lucide-react'
import { Fragment, useEffect } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { toast } from 'sonner'
@@ -10,6 +9,7 @@ import z from 'zod'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
import { FormActions } from 'components/ui/Forms/FormActions'
import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
@@ -24,7 +24,6 @@ import {
AlertTitle_Shadcn_,
Alert_Shadcn_,
Badge,
Button,
FormControl_Shadcn_,
FormDescription_Shadcn_,
FormField_Shadcn_,
@@ -162,15 +161,7 @@ export const ConnectionPooling = () => {
</p>
<Badge>Supavisor</Badge>
</div>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler" />
</div>
}
footer={
@@ -1,4 +1,4 @@
import { ChevronDown, ExternalLink } from 'lucide-react'
import { ChevronDown } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { useParams } from 'common'
@@ -6,6 +6,7 @@ import { getAddons } from 'components/interfaces/Billing/Subscription/Subscripti
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
@@ -17,7 +18,6 @@ import { pluckObjectFields } from 'lib/helpers'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { useDatabaseSettingsStateSnapshot } from 'state/database-settings'
import {
Button,
CollapsibleContent_Shadcn_,
CollapsibleTrigger_Shadcn_,
Collapsible_Shadcn_,
@@ -181,14 +181,7 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
)}
>
<DatabaseSelector />
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
href="https://supabase.com/docs/guides/database/connecting-to-postgres"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/connecting-to-postgres" />
</div>
</div>
<Tabs
@@ -1,10 +1,11 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { AlertCircle, ChevronDown, ExternalLink, Globe, Lock } from 'lucide-react'
import { AlertCircle, ChevronDown, Globe, Lock } from 'lucide-react'
import { useState } from 'react'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import Panel from 'components/ui/Panel'
@@ -97,21 +98,14 @@ const NetworkRestrictions = () => {
return (
<>
<section id="network-restrictions">
<div className="flex items-center justify-between">
<div className="flex items-center justify-between mb-6">
<FormHeader
className="mb-0"
title="Network Restrictions"
description="Allow specific IP ranges to have access to your database."
/>
<div className="flex items-center space-x-2 mb-6">
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/platform/network-restrictions"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
<div className="flex items-center gap-x-2">
<DocsButton href="https://supabase.com/docs/guides/platform/network-restrictions" />
{!canUpdateNetworkRestrictions ? (
<ButtonTooltip
disabled
@@ -1,7 +1,8 @@
import { AlertTriangleIcon, ExternalLink } from 'lucide-react'
import { AlertTriangleIcon } from 'lucide-react'
import { useParams } from 'common'
import { Markdown } from 'components/interfaces/Markdown'
import { DocsButton } from 'components/ui/DocsButton'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { useDatabaseSettingsStateSnapshot } from 'state/database-settings'
@@ -41,15 +42,7 @@ export const PoolingModesModal = () => {
<DialogTitle>
<div className="w-full flex items-center justify-between">
<p className="max-w-2xl">Which pooling mode should I use?</p>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
href="https://supabase.com/docs/guides/database/connecting-to-postgres#how-connection-pooling-works"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/connecting-to-postgres#how-connection-pooling-works" />
</div>
</DialogTitle>
<DialogDescription className="max-w-2xl">
@@ -1,5 +1,5 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Download, ExternalLink, Loader2 } from 'lucide-react'
import { Download, Loader2 } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
@@ -7,6 +7,7 @@ import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
@@ -81,15 +82,9 @@ const SSLConfiguration = () => {
return (
<div id="ssl-configuration">
<div className="flex items-center justify-between">
<FormHeader title="SSL Configuration" description="" />
<div className="flex items-center space-x-2 mb-6">
<Button asChild type="default" icon={<ExternalLink />}>
<Link href="https://supabase.com/docs/guides/platform/ssl-enforcement" target="_blank">
Documentation
</Link>
</Button>
</div>
<div className="flex items-center justify-between mb-6">
<FormHeader className="mb-0" title="SSL Configuration" description="" />
<DocsButton href="https://supabase.com/docs/guides/platform/ssl-enforcement" />
</div>
<FormPanel>
<FormSection
@@ -1,8 +1,8 @@
import { AlertCircle, ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { AlertCircle } from 'lucide-react'
import { useState } from 'react'
import { toast } from 'sonner'
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useCheckCNAMERecordMutation } from 'data/custom-domains/check-cname-mutation'
@@ -89,15 +89,7 @@ const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivate
<Panel.Content className="w-full">
<div className="flex items-center justify-between">
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/platform/custom-domains"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/custom-domains" />
<div className="flex items-center space-x-2">
<Button
type="default"
@@ -1,13 +1,13 @@
import Link from 'next/link'
import { useState } from 'react'
import { toast } from 'sonner'
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domains-delete-mutation'
import type { CustomDomainResponse } from 'data/custom-domains/custom-domains-query'
import { Trash } from 'lucide-react'
import { Button } from 'ui'
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
import { ExternalLink, Trash } from 'lucide-react'
export type CustomDomainDeleteProps = {
projectRef?: string
@@ -49,15 +49,7 @@ const CustomDomainDelete = ({ projectRef, customDomain }: CustomDomainDeleteProp
<Panel.Content className="w-full">
<div className="flex items-center justify-between">
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/platform/custom-domains"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/custom-domains" />
<Button
type="danger"
icon={<Trash />}
@@ -1,8 +1,9 @@
import { AlertCircle, ExternalLink, HelpCircle, RefreshCw } from 'lucide-react'
import { AlertCircle, HelpCircle, RefreshCw } from 'lucide-react'
import Link from 'next/link'
import { useState } from 'react'
import { useParams } from 'common'
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useCustomDomainDeleteMutation } from 'data/custom-domains/custom-domains-delete-mutation'
@@ -182,15 +183,7 @@ const CustomDomainVerify = ({ customDomain }: CustomDomainVerifyProps) => {
<Panel.Content>
<div className="flex items-center justify-between">
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/platform/custom-domains"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/custom-domains" />
<div className="flex items-center space-x-2">
<Button
type="default"
@@ -1,10 +1,9 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import * as yup from 'yup'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { FormActions } from 'components/ui/Forms/FormActions'
import { FormPanel } from 'components/ui/Forms/FormPanel'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
@@ -12,7 +11,7 @@ import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query
import { useCheckCNAMERecordMutation } from 'data/custom-domains/check-cname-mutation'
import { useCustomDomainCreateMutation } from 'data/custom-domains/custom-domains-create-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Button, Form, Input } from 'ui'
import { Form, Input } from 'ui'
const schema = yup.object({
domain: yup.string().required('A value for your custom domain is required'),
@@ -74,15 +73,7 @@ const CustomDomainsConfigureHostname = () => {
!canConfigureCustomDomain ? (
"You need additional permissions to update your project's custom domain settings"
) : (
<Button asChild type="default" icon={<ExternalLink />}>
<Link
href="https://supabase.com/docs/guides/platform/custom-domains"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/custom-domains" />
)
}
/>
@@ -1,16 +1,16 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import Link from 'next/link'
import { Loader, Shield, Users, Wrench } from 'lucide-react'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import { useProjectTransferMutation } from 'data/projects/project-transfer-mutation'
import { useProjectTransferPreviewQuery } from 'data/projects/project-transfer-preview-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useFlag } from 'hooks/ui/useFlag'
import { Loader, Shield, Users, Wrench } from 'lucide-react'
import { Alert, Button, InfoIcon, Listbox, Loading, Modal } from 'ui'
const TransferProjectButton = () => {
@@ -113,20 +113,9 @@ const TransferProjectButton = () => {
<Modal.Content className="text-foreground-light">
<p className="text-sm">
To transfer projects, the owner must be a member of both the source and target
organizations. For further information see our{' '}
<Link
href="https://supabase.com/docs/guides/platform/project-transfer"
className="text-brand hover:underline"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
.
organizations. Consider the following before transferring your project:
</p>
<p className="mt-6 text-sm">Transferring considerations:</p>
<ul className="mt-4 space-y-5 text-sm">
<li className="flex gap-4">
<span className="shrink-0 mt-1">
@@ -167,8 +156,16 @@ const TransferProjectButton = () => {
</div>
</li>
</ul>
<DocsButton
abbrev={false}
className="mt-6"
href="https://supabase.com/docs/guides/platform/project-transfer"
/>
</Modal.Content>
<Modal.Separator />
<Modal.Content>
{organizations && (
<div className="space-y-2">
@@ -204,67 +201,69 @@ const TransferProjectButton = () => {
)}
</Modal.Content>
<Loading active={selectedOrg !== undefined && transferPreviewIsLoading}>
<Modal.Content>
<div className="space-y-2">
{transferPreviewData && transferPreviewData.warnings.length > 0 && (
<Alert
withIcon
variant="warning"
title="Warnings for project transfer"
className="mt-3"
>
<div className="space-y-1">
{transferPreviewData.warnings.map((warning) => (
<p key={warning.key}>{warning.message}</p>
))}
</div>
</Alert>
)}
{transferPreviewData && transferPreviewData.errors.length > 0 && (
<Alert withIcon variant="danger" title="Project cannot be transferred">
<div className="space-y-1">
{transferPreviewData.errors.map((error) => (
<p key={error.key}>{error.message}</p>
))}
</div>
{transferPreviewData.members_exceeding_free_project_limit.length > 0 && (
<div className="space-y-2">
<p className="text-sm text-foreground-light">
These members have reached their maximum limits for the number of active
Free plan projects within organizations where they are an administrator or
owner:
</p>
<ul className="pl-5 text-sm list-disc text-foreground-light">
{(transferPreviewData.members_exceeding_free_project_limit || []).map(
(member, idx: number) => (
<li key={`member-${idx}`}>
{member.name} (Limit: {member.limit} free projects)
</li>
)
)}
</ul>
<p className="text-sm text-foreground-light">
These members will need to either delete, pause, or upgrade one or more of
their projects before you can downgrade this project.
</p>
{selectedOrg !== undefined && (
<Loading active={selectedOrg !== undefined && transferPreviewIsLoading}>
<Modal.Content>
<div className="space-y-2">
{transferPreviewData && transferPreviewData.warnings.length > 0 && (
<Alert
withIcon
variant="warning"
title="Warnings for project transfer"
className="mt-3"
>
<div className="space-y-1">
{transferPreviewData.warnings.map((warning) => (
<p key={warning.key}>{warning.message}</p>
))}
</div>
)}
</Alert>
)}
{transferPreviewError && !transferError && (
<Alert withIcon variant="danger" title="Project cannot be transferred">
<p>{transferPreviewError.message}</p>
</Alert>
)}
{transferError && (
<Alert withIcon variant="danger" title="Project cannot be transferred">
<p>{transferError.message}</p>
</Alert>
)}
</div>
</Modal.Content>
</Loading>
</Alert>
)}
{transferPreviewData && transferPreviewData.errors.length > 0 && (
<Alert withIcon variant="danger" title="Project cannot be transferred">
<div className="space-y-1">
{transferPreviewData.errors.map((error) => (
<p key={error.key}>{error.message}</p>
))}
</div>
{transferPreviewData.members_exceeding_free_project_limit.length > 0 && (
<div className="space-y-2">
<p className="text-sm text-foreground-light">
These members have reached their maximum limits for the number of active
Free plan projects within organizations where they are an administrator or
owner:
</p>
<ul className="pl-5 text-sm list-disc text-foreground-light">
{(transferPreviewData.members_exceeding_free_project_limit || []).map(
(member, idx: number) => (
<li key={`member-${idx}`}>
{member.name} (Limit: {member.limit} free projects)
</li>
)
)}
</ul>
<p className="text-sm text-foreground-light">
These members will need to either delete, pause, or upgrade one or more of
their projects before you can downgrade this project.
</p>
</div>
)}
</Alert>
)}
{transferPreviewError && !transferError && (
<Alert withIcon variant="danger" title="Project cannot be transferred">
<p>{transferPreviewError.message}</p>
</Alert>
)}
{transferError && (
<Alert withIcon variant="danger" title="Project cannot be transferred">
<p>{transferError.message}</p>
</Alert>
)}
</div>
</Modal.Content>
</Loading>
)}
</Modal>
</>
)
@@ -21,6 +21,7 @@ import {
} from 'components/layouts/Scaffold'
import DateRangePicker from 'components/to-be-cleaned/DateRangePicker'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import { DocsButton } from 'components/ui/DocsButton'
import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { DataPoint } from 'data/analytics/constants'
@@ -33,7 +34,6 @@ import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { INSTANCE_MICRO_SPECS, INSTANCE_NANO_SPECS } from 'lib/constants'
import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants/metrics'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { INFRA_ACTIVITY_METRICS } from './Infrastructure.constants'
@@ -397,11 +397,10 @@ const InfrastructureActivity = () => {
add-ons are designed for sustained, high performance with specific IOPS and
throughput limits without needing to burst."
>
<Button asChild type="default" icon={<ExternalLink />}>
<Link href="https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops">
Documentation
</Link>
</Button>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops"
/>
</Admonition>
</>
) : chartMeta[attribute.key].isLoading ? (
@@ -1,4 +1,4 @@
import { ChevronDown, ExternalLink } from 'lucide-react'
import { ChevronDown } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
@@ -12,6 +12,7 @@ import {
calculateIOPSPrice,
calculateThroughputPrice,
} from 'components/interfaces/DiskManagement/DiskManagement.utils'
import { DocsButton } from 'components/ui/DocsButton'
import { useDiskAttributesQuery } from 'data/config/disk-attributes-query'
import { useEnablePhysicalBackupsMutation } from 'data/database/enable-physical-backups-mutation'
import { useOverdueInvoicesQuery } from 'data/invoices/invoices-overdue-query'
@@ -51,7 +52,6 @@ import {
cn,
} from 'ui'
import { AVAILABLE_REPLICA_REGIONS } from './InstanceConfiguration.constants'
import { Admonition } from 'ui-patterns'
// [Joshen] FYI this is purely for AWS only, need to update to support Fly eventually
@@ -242,7 +242,7 @@ const DeployNewReplicaPanel = ({
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
) : !isAWSProvider ? (
) : isAWSProvider ? (
<Alert_Shadcn_>
<WarningIcon />
<AlertTitle_Shadcn_>
@@ -253,17 +253,11 @@ const DeployNewReplicaPanel = ({
Projects provisioned by other cloud providers currently will not be able to use read
replicas
</span>
<div className="mt-3">
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/platform/read-replicas#prerequisites"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
</div>
<DocsButton
abbrev={false}
className="mt-3"
href="https://supabase.com/docs/guides/platform/read-replicas#prerequisites"
/>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
) : currentPgVersion < 15 ? (
@@ -310,15 +304,10 @@ const DeployNewReplicaPanel = ({
{isFreePlan ? 'Upgrade to Pro' : 'Change compute size'}
</Link>
</Button>
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/platform/read-replicas#prerequisites"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/platform/read-replicas#prerequisites"
/>
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
@@ -356,15 +345,10 @@ const DeployNewReplicaPanel = ({
>
Enable physical backups
</Button>
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/platform/read-replicas#how-are-read-replicas-made"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/platform/read-replicas#how-are-read-replicas-made"
/>
</AlertDescription_Shadcn_>
)}
</Alert_Shadcn_>
@@ -23,6 +23,7 @@ import {
DialogTrigger,
} from 'ui'
import { AVAILABLE_REPLICA_REGIONS } from './InstanceConfiguration.constants'
import { DocsButton } from 'components/ui/DocsButton'
interface EnablePhysicalBackupsModalProps {
selectedRegion: string
@@ -123,15 +124,7 @@ export const EnablePhysicalBackupsModal = ({ selectedRegion }: EnablePhysicalBac
</DialogSection>
{!enabling && (
<DialogFooter>
<Button asChild type="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/platform/read-replicas#how-are-read-replicas-made"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/read-replicas#how-are-read-replicas-made" />
<Button
type="primary"
loading={isEnabling}
@@ -2,18 +2,19 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import dayjs from 'dayjs'
import { sortBy } from 'lodash'
import Link from 'next/link'
import { Fragment, useEffect, useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { usePgSodiumKeyCreateMutation } from 'data/pg-sodium-keys/pg-sodium-key-create-mutation'
import { usePgSodiumKeyDeleteMutation } from 'data/pg-sodium-keys/pg-sodium-key-delete-mutation'
import { usePgSodiumKeysQuery } from 'data/pg-sodium-keys/pg-sodium-keys-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Key, Loader, Search, Trash, X } from 'lucide-react'
import { Alert, Button, Form, Input, Listbox, Modal, Separator } from 'ui'
import { Search, X, ExternalLink, Loader, Key, Trash } from 'lucide-react'
const DEFAULT_KEY_NAME = 'No description provided'
@@ -135,44 +136,21 @@ const EncryptionKeysManagement = () => {
</Listbox>
</div>
</div>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/database/vault"
target="_blank"
rel="noreferrer"
>
Vault Documentation
</Link>
</Button>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>
<Button
type="primary"
disabled={!canManageKeys}
onClick={() => setShowAddKeyModal(true)}
>
Add new key
</Button>
</Tooltip.Trigger>
{!canManageKeys && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">
You need additional permissions to add keys
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
)}
</Tooltip.Root>
<div className="flex items-center gap-x-2">
<DocsButton href="https://supabase.com/docs/guides/database/vault" />
<ButtonTooltip
type="primary"
disabled={!canManageKeys}
onClick={() => setShowAddKeyModal(true)}
tooltip={{
content: {
side: 'bottom',
text: !canManageKeys ? 'You need additional permissions to add keys' : undefined,
},
}}
>
Add new key
</ButtonTooltip>
</div>
</div>
@@ -1,11 +1,12 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { sortBy } from 'lodash'
import Link from 'next/link'
import { Loader, Search, X } from 'lucide-react'
import { Fragment, useEffect, useState } from 'react'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DocsButton } from 'components/ui/DocsButton'
import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import type { VaultSecret } from 'types'
@@ -14,7 +15,6 @@ import AddNewSecretModal from './AddNewSecretModal'
import DeleteSecretModal from './DeleteSecretModal'
import EditSecretModal from './EditSecretModal'
import SecretRow from './SecretRow'
import { Search, X, ExternalLink, Loader } from 'lucide-react'
const SecretsManagement = () => {
const { search } = useParams()
@@ -110,44 +110,23 @@ const SecretsManagement = () => {
</Listbox>
</div>
</div>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/database/vault"
target="_blank"
rel="noreferrer"
>
Vault Documentation
</Link>
</Button>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>
<Button
type="primary"
disabled={!canManageSecrets}
onClick={() => setShowAddSecretModal(true)}
>
Add new secret
</Button>
</Tooltip.Trigger>
{!canManageSecrets && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">
You need additional permissions to add secrets
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
)}
</Tooltip.Root>
<div className="flex items-center gap-x-2">
<DocsButton href="https://supabase.com/docs/guides/database/vault" />
<ButtonTooltip
type="primary"
disabled={!canManageSecrets}
onClick={() => setShowAddSecretModal(true)}
tooltip={{
content: {
side: 'bottom',
text: !canManageSecrets
? 'You need additional permissions to add secrets'
: undefined,
},
}}
>
Add new secret
</ButtonTooltip>
</div>
</div>
@@ -1,6 +1,6 @@
import type { PostgresTable } from '@supabase/postgres-meta'
import { sortBy } from 'lodash'
import { ArrowRight, Database, ExternalLink, HelpCircle, Table, X } from 'lucide-react'
import { ArrowRight, Database, HelpCircle, Table, X } from 'lucide-react'
import { Fragment, useEffect, useState } from 'react'
import {
AlertDescription_Shadcn_,
@@ -12,6 +12,7 @@ import {
} from 'ui'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import InformationBox from 'components/ui/InformationBox'
import { FOREIGN_KEY_CASCADE_ACTION } from 'data/database/database-query-constants'
import { useSchemasQuery } from 'data/database/schemas-query'
@@ -498,15 +499,7 @@ export const ForeignKeySelector = ({
label="Action if referenced row is removed"
// @ts-ignore
labelOptional={
<Button asChild type="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/postgres/cascade-deletes"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/postgres/cascade-deletes" />
}
descriptionText={
<>
@@ -1,62 +1,53 @@
import { AlertOctagon, BookOpen, Lock, ShieldOff } from 'lucide-react'
import Link from 'next/link'
import { Alert, Button } from 'ui'
import { AlertOctagon, Lock, ShieldOff } from 'lucide-react'
import { DocsButton } from 'components/ui/DocsButton'
import { Alert } from 'ui'
export default function RLSDisableModalContent() {
return (
<div className="my-6">
<div className="text-sm text-foreground-light grid gap-4">
<div className="grid gap-1">
<Alert
variant="warning"
className="!px-4 !py-3"
title="This table will be publicly readable and writable"
withIcon
>
<p>Anyone can edit or delete data in this table.</p>
</Alert>
<ul className="mt-4 space-y-5">
<li className="flex gap-3">
<AlertOctagon />
<span>All requests to this table will be accepted.</span>
</li>
<div className="text-sm text-foreground-light grid gap-4">
<div className="grid gap-1">
<Alert
variant="warning"
className="!px-4 !py-3"
title="This table will be publicly readable and writable"
withIcon
>
<p>Anyone can edit or delete data in this table.</p>
</Alert>
<ul className="mt-4 space-y-5">
<li className="flex gap-3">
<AlertOctagon />
<span>All requests to this table will be accepted.</span>
</li>
<li className="flex gap-3">
<ShieldOff />
<span>Auth policies will not be enforced.</span>
</li>
<li className="flex gap-3">
<ShieldOff />
<span>Auth policies will not be enforced.</span>
</li>
<li className="flex gap-3">
<Lock size={14} className="flex-shrink-0" />
<div>
<strong>Before you turn off Row Level Security, consider:</strong>
<ul className="space-y-2 mt-2">
<li className="list-disc ml-4">
Any personal information in this table will be publicly accessible.
</li>
<li className="list-disc ml-4">
Anyone will be able to modify, add or delete any row in this table.
</li>
</ul>
</div>
</li>
</ul>
</div>
<div className="mt-3">
<p className="mt-2">
<Button asChild type="default" icon={<BookOpen strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/auth/row-level-security"
target="_blank"
rel="noreferrer"
>
RLS Documentation
</Link>
</Button>
</p>
</div>
<li className="flex gap-3">
<Lock size={14} className="flex-shrink-0" />
<div>
<strong>Before you turn off Row Level Security, consider:</strong>
<ul className="space-y-2 mt-2">
<li className="list-disc ml-4">
Any personal information in this table will be publicly accessible.
</li>
<li className="list-disc ml-4">
Anyone will be able to modify, add or delete any row in this table.
</li>
</ul>
</div>
</li>
</ul>
</div>
<DocsButton
abbrev={false}
className="w-min mt-3"
href="https://supabase.com/docs/guides/auth/row-level-security"
/>
</div>
)
}
@@ -2,10 +2,11 @@ import type { PostgresTable } from '@supabase/postgres-meta'
import { isEmpty, isUndefined, noop } from 'lodash'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { Alert, Badge, Button, Checkbox, Input, SidePanel } from 'ui'
import { Badge, Checkbox, Input, SidePanel } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { useDatabasePublicationsQuery } from 'data/database-publications/database-publications-query'
import {
CONSTRAINT_TYPE,
@@ -21,7 +22,8 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { useUrlState } from 'hooks/ui/useUrlState'
import { EXCLUDED_SCHEMAS_WITHOUT_EXTENSIONS } from 'lib/constants/schemas'
import { ExternalLink } from 'lucide-react'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import { Admonition } from 'ui-patterns'
import ActionBar from '../ActionBar'
import type { ForeignKey } from '../ForeignKeySelector/ForeignKeySelector.types'
import { formatForeignKeys } from '../ForeignKeySelector/ForeignKeySelector.utils'
@@ -39,7 +41,6 @@ import {
generateTableFieldFromPostgresTable,
validateFields,
} from './TableEditor.utils'
import { useTableEditorStateSnapshot } from 'state/table-editor'
export interface TableEditorProps {
table?: PostgresTable
@@ -304,49 +305,41 @@ const TableEditor = ({
size="medium"
/>
{tableFields.isRLSEnabled ? (
<Alert
withIcon
variant="info"
className="!px-4 !py-3 !mt-3"
<Admonition
type="default"
className="!mt-3"
title="Policies are required to query data"
description={
<>
You need to create an access policy before you can query data from this table.
Without a policy, querying this table will return an{' '}
<u className="text-foreground">empty array</u> of results.{' '}
{isNewRecord ? 'You can create policies after saving this table.' : ''}
</>
}
>
<p>
You need to create an access policy before you can query data from this table. Without
a policy, querying this table will return an{' '}
<u className="text-foreground">empty array</u> of results.{' '}
{isNewRecord ? 'You can create policies after saving this table.' : ''}
</p>
<Button asChild type="default" icon={<ExternalLink />} className="mt-4">
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/auth/row-level-security"
>
RLS Documentation
</a>
</Button>
</Alert>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/auth/row-level-security"
/>
</Admonition>
) : (
<Alert
withIcon
variant="warning"
className="!px-4 !py-3 mt-3"
<Admonition
type="warning"
className="!mt-3"
title="You are allowing anonymous access to your table"
description={
<>
{tableFields.name ? `The table ${tableFields.name}` : 'Your table'} will be publicly
writable and readable
</>
}
>
<p>
{tableFields.name ? `The table ${tableFields.name}` : 'Your table'} will be publicly
writable and readable
</p>
<Button asChild type="default" icon={<ExternalLink />} className="mt-4">
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/auth/row-level-security"
>
RLS Documentation
</a>
</Button>
</Alert>
<DocsButton
abbrev={false}
href="https://supabase.com/docs/guides/auth/row-level-security"
/>
</Admonition>
)}
{realtimeEnabled && (
<Checkbox
@@ -2,7 +2,6 @@ import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { QueryKey, useQueryClient } from '@tanstack/react-query'
import { find, isUndefined } from 'lodash'
import { ExternalLink } from 'lucide-react'
import { useRouter } from 'next/router'
import { toast } from 'sonner'
@@ -11,6 +10,7 @@ import { SupabaseGrid } from 'components/grid/SupabaseGrid'
import { getSupaTable } from 'components/grid/SupabaseGrid.utils'
import { SupaTable } from 'components/grid/types'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
import { useEncryptedColumnsQuery } from 'data/encrypted-columns/encrypted-columns-query'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
@@ -26,7 +26,6 @@ import { EMPTY_ARR } from 'lib/void'
import { useGetImpersonatedRole } from 'state/role-impersonation-state'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import type { Dictionary } from 'types'
import { Button } from 'ui'
import GridHeaderActions from './GridHeaderActions'
import { TableGridSkeletonLoader } from './LoadingState'
import NotFoundState from './NotFoundState'
@@ -214,15 +213,7 @@ const TableGridEditor = ({
row before updating or deleting the row.
</p>
<div className="mt-3">
<Button asChild type="outline" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/tables#primary-keys"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/tables#primary-keys" />
</div>
</div>
),
@@ -26,6 +26,7 @@ import BranchingPITRNotice from './BranchingPITRNotice'
import BranchingPlanNotice from './BranchingPlanNotice'
import BranchingPostgresVersionNotice from './BranchingPostgresVersionNotice'
import GithubRepositorySelection from './GithubRepositorySelection'
import { DocsButton } from 'components/ui/DocsButton'
const EnableBranchingModal = () => {
const { ref } = useParams()
@@ -152,15 +153,7 @@ const EnableBranchingModal = () => {
<p className="text-sm text-foreground-light">Manage environments in Supabase</p>
</div>
</div>
<Button type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link
href="https://supabase.com/docs/guides/platform/branching"
target="_blank"
rel="noreferrer"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/branching" />
</Modal.Content>
{isLoading && (
@@ -14,6 +14,7 @@ import { Code, ExternalLink } from 'lucide-react'
import { Button } from 'ui'
import FunctionsNav from '../../interfaces/Functions/FunctionsNav'
import ProjectLayout from '../ProjectLayout/ProjectLayout'
import { DocsButton } from 'components/ui/DocsButton'
interface FunctionsLayoutProps {
title?: string
@@ -121,16 +122,10 @@ const FunctionsLayout = ({ title, children }: PropsWithChildren<FunctionsLayoutP
}
/>
)}
<Button
asChild
type="default"
<DocsButton
className="translate-y-[1px]"
icon={<ExternalLink size={14} strokeWidth={1.5} />}
>
<Link href="https://supabase.com/docs/guides/functions" target="_link">
Documentation
</Link>
</Button>
href="https://supabase.com/docs/guides/functions"
/>
</div>
</div>
</div>
@@ -11,6 +11,7 @@ import { BASE_PATH } from 'lib/constants'
import { auth, buildPathWithParams, getAccessToken, getReturnToPath } from 'lib/gotrue'
import { tweets } from 'shared-data'
import { Button } from 'ui'
import { DocsButton } from 'components/ui/DocsButton'
type SignInLayoutProps = {
heading: string
@@ -109,11 +110,7 @@ const SignInLayout = ({
</div>
<div className="items-center hidden space-x-3 md:ml-10 md:flex md:pr-4">
<Button asChild type="default" icon={<FileText />}>
<Link href="https://supabase.com/docs" target="_blank" rel="noreferrer">
Documentation
</Link>
</Button>
<DocsButton abbrev={false} href="https://supabase.com/docs" />
</div>
</nav>
</div>
@@ -5,7 +5,9 @@ import { toast } from 'sonner'
import { POLICY_MODAL_VIEWS } from 'components/interfaces/Auth/Policies/Policies.constants'
import PolicySelection from 'components/interfaces/Auth/Policies/PolicySelection'
import PolicyTemplates from 'components/interfaces/Auth/Policies/PolicyTemplates'
import { Button, Modal } from 'ui'
import { DocsButton } from 'components/ui/DocsButton'
import { ChevronLeft } from 'lucide-react'
import { Modal } from 'ui'
import {
applyBucketIdToTemplateDefinition,
createPayloadsForAddPolicy,
@@ -14,7 +16,6 @@ import {
import { STORAGE_POLICY_TEMPLATES } from './StoragePolicies.constants'
import StoragePoliciesEditor from './StoragePoliciesEditor'
import StoragePoliciesReview from './StoragePoliciesReview'
import { ChevronLeft, ExternalLink } from 'lucide-react'
const newPolicyTemplate: any = {
name: '',
@@ -181,18 +182,9 @@ const StoragePoliciesEditPolicyModal = ({
)
}
return (
<div className="w-full flex items-center justify-between gap-x-2">
<div className="w-full flex items-center justify-between gap-x-2 pr-6">
<h4 className="m-0 truncate">{getTitle()}</h4>
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/learn/auth-deep-dive/auth-policies"
target="_blank"
rel="noreferrer"
>
{' '}
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/learn/auth-deep-dive/auth-policies" />
</div>
)
}
@@ -200,6 +192,7 @@ const StoragePoliciesEditPolicyModal = ({
return (
<Modal
hideFooter
className="[&>div:first-child]:py-3"
size={view === POLICY_MODAL_VIEWS.SELECTION ? 'medium' : 'xxlarge'}
visible={visible}
contentStyle={{ padding: 0 }}
@@ -1,7 +1,7 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { AnimatePresence, motion } from 'framer-motion'
import { last } from 'lodash'
import { ExternalLink, FileText, MessageCircleMore, Plus, WandSparkles } from 'lucide-react'
import { FileText, MessageCircleMore, Plus, WandSparkles } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useMemo, useRef, useState } from 'react'
import { toast } from 'sonner'
@@ -13,6 +13,7 @@ import OptInToOpenAIToggle from 'components/interfaces/Organization/GeneralSetti
import { MessageWithDebug } from 'components/interfaces/SQLEditor/AiAssistantPanel'
import { DiffType } from 'components/interfaces/SQLEditor/SQLEditor.types'
import { useSqlDebugMutation } from 'data/ai/sql-debug-mutation'
import { useEntityDefinitionQuery } from 'data/database/entity-definition-query'
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
import { useOrganizationUpdateMutation } from 'data/organizations/organization-update-mutation'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
@@ -53,6 +54,7 @@ import {
} from 'ui'
import { Admonition, AssistantChatForm } from 'ui-patterns'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { DocsButton } from '../DocsButton'
import { ASSISTANT_SUPPORT_ENTITIES } from './AiAssistant.constants'
import { SupportedAssistantEntities, SupportedAssistantQuickPromptTypes } from './AIAssistant.types'
import { generatePrompt, retrieveDocsUrl } from './AIAssistant.utils'
@@ -60,7 +62,6 @@ import { ContextBadge } from './ContextBadge'
import { EntitiesDropdownMenu } from './EntitiesDropdownMenu'
import { Message } from './Message'
import { SchemasDropdownMenu } from './SchemasDropdownMenu'
import { useEntityDefinitionQuery } from 'data/database/entity-definition-query'
const ANIMATION_DURATION = 0.3
@@ -702,13 +703,7 @@ export const AIAssistant = ({
?
</TooltipContent_Shadcn_>
</Tooltip_Shadcn_>
{docsUrl !== undefined && (
<Button asChild type="default" icon={<ExternalLink />}>
<a href={docsUrl} target="_blank" rel="noreferrer">
Documentation
</a>
</Button>
)}
{docsUrl !== undefined && <DocsButton href={docsUrl} />}
</div>
</motion.div>
)}
+18
View File
@@ -0,0 +1,18 @@
import { BookOpen } from 'lucide-react'
import { Button } from 'ui'
interface DocsButtonProps {
href: string
abbrev?: boolean
className?: string
}
export const DocsButton = ({ href, abbrev = true, className }: DocsButtonProps) => {
return (
<Button asChild type="default" className={className} icon={<BookOpen />}>
<a target="_blank" rel="noopener noreferrer" href={href}>
{abbrev ? 'Docs' : 'Documentation'}
</a>
</Button>
)
}
+3 -10
View File
@@ -1,10 +1,9 @@
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import ReactMarkdown from 'react-markdown'
import { Markdown } from 'components/interfaces/Markdown'
import { ReactNode } from 'react'
import { Button, cn } from 'ui'
import { cn } from 'ui'
import { DocsButton } from '../DocsButton'
const FormHeader = ({
title,
@@ -30,13 +29,7 @@ const FormHeader = ({
{description && <Markdown content={description} className="max-w-full" />}
</div>
<div className="flex items-center gap-x-2">
{docsUrl !== undefined && (
<Button asChild type="default" icon={<ExternalLink />}>
<Link href={docsUrl} target="_blank" rel="noreferrer">
Documentation
</Link>
</Button>
)}
{docsUrl !== undefined && <DocsButton href={docsUrl} />}
{actions}
</div>
</div>
@@ -8,6 +8,7 @@ import { copyToClipboard } from 'lib/helpers'
import { BookOpen, Check, Clipboard, ExternalLink, List, X } from 'lucide-react'
import { logConstants } from 'shared-data'
import { Button, SidePanel, Tabs } from 'ui'
import { DocsButton } from '../DocsButton'
export interface LogsExplorerHeaderProps {
subtitle?: string
@@ -31,9 +32,7 @@ const LogsExplorerHeader = ({ subtitle }: LogsExplorerHeaderProps) => {
{subtitle && <span className="text-2xl text-foreground-light">{subtitle}</span>}
</div>
<div className="flex flex-row gap-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<Link href={LOGS_EXPLORER_DOCS_URL}>Documentation</Link>
</Button>
<DocsButton href={LOGS_EXPLORER_DOCS_URL} />
<SidePanel
size="large"
@@ -1,11 +1,10 @@
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
import { ExternalLink } from 'lucide-react'
import { toast } from 'sonner'
import { Button } from 'ui'
import { Query } from 'components/grid/query/Query'
import type { SupaRow } from 'components/grid/types'
import { Markdown } from 'components/interfaces/Markdown'
import { DocsButton } from 'components/ui/DocsButton'
import { executeSql } from 'data/sql/execute-sql-query'
import { sqlKeys } from 'data/sql/keys'
import type { Table } from 'data/tables/table-query'
@@ -109,15 +108,7 @@ export const useTableRowDeleteMutation = ({
View "{referencingTable}" table
</Link>
</Button> */}
<Button asChild type="outline" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/postgres/cascade-deletes"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/postgres/cascade-deletes" />
</div>
),
})
@@ -130,15 +121,7 @@ export const useTableRowDeleteMutation = ({
each row before updating or deleting the row.
</p>
<div className="mt-3">
<Button asChild type="outline" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/tables#primary-keys"
>
Documentation
</a>
</Button>
<DocsButton href="https://supabase.com/docs/guides/database/tables#primary-keys" />
</div>
</div>
),
@@ -20,6 +20,7 @@ import { useUrlState } from 'hooks/ui/useUrlState'
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
import type { NextPageWithLayout } from 'types'
import { Button, Input } from 'ui'
import { DocsButton } from 'components/ui/DocsButton'
/**
* Filter tables by table name and policy name
@@ -132,17 +133,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
icon={<Search size={14} />}
/>
</div>
<div className="flex items-center gap-x-2">
<Button type="default" icon={<ExternalLink strokeWidth={1.5} />} asChild>
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security"
>
Documentation
</a>
</Button>
</div>
<DocsButton href="https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security" />
</div>
</div>
@@ -1,5 +1,5 @@
import { useParams } from 'common'
import { AlertCircle, ExternalLink, XIcon } from 'lucide-react'
import { AlertCircle, XIcon } from 'lucide-react'
import Link from 'next/link'
import { useCallback, useMemo, useState } from 'react'
import { toast } from 'sonner'
@@ -18,6 +18,7 @@ import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { PgRole, useDatabaseRolesQuery } from 'data/database-roles/database-roles-query'
import { useColumnPrivilegesQuery } from 'data/privileges/column-privileges-query'
@@ -227,17 +228,7 @@ const PrivilegesPage: NextPageWithLayout = () => {
<p>Grant or revoke privileges on a column based on user role.</p>
</div>
</div>
<div className="flex items-center space-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
href="https://supabase.com/docs/guides/auth/column-level-security"
target="_blank"
rel="noreferrer"
>
Documentation
</a>
</Button>
</div>
<DocsButton href="https://supabase.com/docs/guides/auth/column-level-security" />
</div>
{isEnabled ? (
@@ -8,6 +8,7 @@ import {
ScaffoldSectionContent,
ScaffoldSectionDetail,
} from 'components/layouts/Scaffold'
import { DocsButton } from 'components/ui/DocsButton'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import type { NextPageWithLayout } from 'types'
import { Button } from 'ui'
@@ -24,16 +25,10 @@ const IndexesPage: NextPageWithLayout = () => {
/>
</ScaffoldSectionContent>
<ScaffoldSectionDetail className="flex items-center justify-end gap-2 flex-wrap">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
className="no-underline"
href="https://supabase.com/docs/guides/database/query-optimization"
>
Documentation
</a>
</Button>
<DocsButton
className="no-underline"
href="https://supabase.com/docs/guides/database/query-optimization"
/>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
@@ -11,6 +11,7 @@ import {
import { FormHeader } from 'components/ui/Forms/FormHeader'
import type { NextPageWithLayout } from 'types'
import { Button } from 'ui'
import { DocsButton } from 'components/ui/DocsButton'
const MigrationsPage: NextPageWithLayout = () => {
return (
@@ -24,16 +25,10 @@ const MigrationsPage: NextPageWithLayout = () => {
/>
</ScaffoldSectionContent>
<ScaffoldSectionDetail className="flex items-center justify-end gap-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
className="no-underline"
href="https://supabase.com/docs/guides/getting-started/local-development#database-migrations"
>
Documentation
</a>
</Button>
<DocsButton
className="no-underline"
href="https://supabase.com/docs/guides/getting-started/local-development#database-migrations"
/>
</ScaffoldSectionDetail>
<div className="col-span-12 mt-3">
<Migrations />
@@ -11,6 +11,7 @@ import {
import { FormHeader } from 'components/ui/Forms/FormHeader'
import type { NextPageWithLayout } from 'types'
import { Button } from 'ui'
import { DocsButton } from 'components/ui/DocsButton'
const DatabaseEnumeratedTypes: NextPageWithLayout = () => {
return (
@@ -23,18 +24,6 @@ const DatabaseEnumeratedTypes: NextPageWithLayout = () => {
description="Custom data types that you can use in your database tables or functions."
/>
</ScaffoldSectionContent>
<ScaffoldSectionDetail className="flex items-center justify-end">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
className="no-underline"
href="https://www.postgresql.org/docs/current/datatype-enum.html"
>
Documentation
</a>
</Button>
</ScaffoldSectionDetail>
<div className="col-span-12 mt-3">
<EnumeratedTypes />
</div>
@@ -19,6 +19,7 @@ import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPe
import type { NextPageWithLayout } from 'types'
import { Button } from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { DocsButton } from 'components/ui/DocsButton'
const DatabaseWrappers: NextPageWithLayout = () => {
const { project } = useProjectContext()
@@ -53,16 +54,10 @@ const DatabaseWrappers: NextPageWithLayout = () => {
/>
</ScaffoldSectionContent>
<ScaffoldSectionDetail className="xl:!col-span-4 flex items-center justify-end gap-x-2">
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />}>
<a
target="_blank"
rel="noreferrer"
className="no-underline"
href="https://supabase.com/docs/guides/database/extensions/wrappers/overview"
>
Documentation
</a>
</Button>
<DocsButton
className="no-underline"
href="https://supabase.com/docs/guides/database/extensions/wrappers/overview"
/>
{isWrappersEnabled && <WrappersDropdown />}
</ScaffoldSectionDetail>
@@ -1,6 +1,4 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { ExternalLink } from 'lucide-react'
import Link from 'next/link'
import { useState } from 'react'
import { toast } from 'sonner'
@@ -15,6 +13,7 @@ import {
ScaffoldHeader,
ScaffoldTitle,
} from 'components/layouts/Scaffold'
import { DocsButton } from 'components/ui/DocsButton'
import { useCreateLogDrainMutation } from 'data/log-drains/create-log-drain-mutation'
import { LogDrainData, useLogDrainsQuery } from 'data/log-drains/log-drains-query'
import { useUpdateLogDrainMutation } from 'data/log-drains/update-log-drain-mutation'
@@ -89,15 +88,8 @@ const LogDrainsSettings: NextPageWithLayout = () => {
</ScaffoldDescription>
</div>
<div className="flex items-center justify-end gap-2">
<Button type="default" icon={<ExternalLink strokeWidth={1.5} />} asChild>
<Link
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/platform/log-drains"
>
Documentation
</Link>
</Button>
<DocsButton href="https://supabase.com/docs/guides/platform/log-drains" />
{!(logDrains?.length === 0) && (
<Button
disabled={!logDrainsEnabled || !canManageLogDrains}