mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Update all documentation buttons (#30163)
This commit is contained in:
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 && (
|
||||
|
||||
+7
-11
@@ -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">
|
||||
|
||||
+5
-12
@@ -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>
|
||||
}
|
||||
>
|
||||
|
||||
+3
-11
@@ -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} />
|
||||
|
||||
+3
-10
@@ -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">
|
||||
|
||||
-232
@@ -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
-11
@@ -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={
|
||||
|
||||
+3
-10
@@ -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
|
||||
|
||||
+6
-12
@@ -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
|
||||
|
||||
+3
-11
@@ -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"
|
||||
|
||||
+3
-11
@@ -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 />}
|
||||
|
||||
+3
-10
@@ -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"
|
||||
|
||||
+3
-12
@@ -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" />
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
+73
-74
@@ -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>
|
||||
</>
|
||||
)
|
||||
|
||||
+5
-6
@@ -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 ? (
|
||||
|
||||
+16
-32
@@ -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_>
|
||||
|
||||
+2
-9
@@ -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}
|
||||
|
||||
+18
-40
@@ -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>
|
||||
|
||||
|
||||
+3
-10
@@ -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={
|
||||
<>
|
||||
|
||||
+44
-53
@@ -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>
|
||||
)
|
||||
}
|
||||
+34
-41
@@ -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>
|
||||
),
|
||||
|
||||
+2
-9
@@ -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>
|
||||
|
||||
+6
-13
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
Reference in new issue
Block a user