Merge branch 'master' into feature/database-tests

This commit is contained in:
Saxon Fletcher committed 2025-08-06 15:21:04 +10:00
commit b66d71dc02
489 files changed
+6547 -4952

No files matched your search

-3
View File
@@ -5,9 +5,6 @@
/apps/studio/ @supabase/Dashboard
# temporary until we've ironed out all the problems with sync workflow
/apps/docs/content/troubleshooting/ @charislam
/apps/www/ @supabase/marketing
/apps/www/public/images/blog @supabase/marketing
/apps/www/lib/redirects.js
+3 -3
View File
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const AiGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('ai')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('ai') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['ai', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const ApiGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('api')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('api') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['api', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const AuthGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('auth')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('auth') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['auth', ...(params.slug ?? [])])
)
@@ -0,0 +1,15 @@
import SectionTroubleshootingPage, {
generateSectionTroubleshootingMetadata,
} from '~/features/docs/TroubleshootingSection.page'
export default async function AuthTroubleshootingPage() {
return (
<SectionTroubleshootingPage
topic="auth"
sectionName="Auth"
description="Search or browse troubleshooting guides for common authentication issues, including login problems, session management, and provider configuration."
/>
)
}
export const metadata = generateSectionTroubleshootingMetadata('auth', 'Auth')
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const CronGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('cron')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('cron') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['cron', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const DatabaseGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('database')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('database') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['database', ...(params.slug ?? [])])
)
@@ -22,8 +22,7 @@ import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import { octokit } from '~/lib/octokit'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
export const dynamicParams = false
import { IS_PROD } from 'common'
// We fetch these docs at build time from an external repo
const org = 'supabase'
@@ -416,6 +415,10 @@ const urlTransform: UrlTransformFunction = (url) => {
}
const generateStaticParams = async () => {
if (IS_PROD) {
return []
}
const mdxPaths = await genGuidesStaticParams('database/extensions/wrappers')()
const federatedPaths = pageMap.map(({ slug }) => ({
slug: [slug],
@@ -0,0 +1,15 @@
import SectionTroubleshootingPage, {
generateSectionTroubleshootingMetadata,
} from '~/features/docs/TroubleshootingSection.page'
export default async function DatabaseTroubleshootingPage() {
return (
<SectionTroubleshootingPage
topic="database"
sectionName="Database"
description="Search or browse troubleshooting guides for common database issues, including connection problems, query optimization, and configuration."
/>
)
}
export const metadata = generateSectionTroubleshootingMetadata('database', 'Database')
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const DeploymentGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('deployment')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('deployment') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['deployment', ...(params.slug ?? [])])
)
@@ -17,8 +17,8 @@ import {
terraformDocsRepo,
} from '../terraformConstants'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
// Each external docs page is mapped to a local page
const pageMap = [
@@ -142,7 +142,9 @@ const getContent = async ({ slug }: Params) => {
}
}
const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
const generateStaticParams = IS_PROD
? async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
: getEmptyArray
const generateMetadata = genGuideMeta(getContent)
export default TerraformDocs
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const FunctionsGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('functions')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('functions') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['functions', ...(params.slug ?? [])])
)
@@ -0,0 +1,15 @@
import SectionTroubleshootingPage, {
generateSectionTroubleshootingMetadata,
} from '~/features/docs/TroubleshootingSection.page'
export default async function FunctionsTroubleshootingPage() {
return (
<SectionTroubleshootingPage
topic="functions"
sectionName="Edge Functions"
description="Search or browse troubleshooting guides for common Edge Functions issues, including deployment problems, runtime errors, and environment configuration."
/>
)
}
export const metadata = generateSectionTroubleshootingMetadata('functions', 'Edge Functions')
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const GettingStartedGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('getting-started')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('getting-started') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['getting-started', ...(params.slug ?? [])])
)
@@ -10,8 +10,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
// We fetch these docs at build time from an external repo
const org = 'supabase'
@@ -190,7 +190,9 @@ const urlTransform: UrlTransformFunction = (url) => {
}
}
const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
const generateStaticParams = IS_PROD
? async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
: getEmptyArray
const generateMetadata = genGuideMeta(getContent)
export default PGGraphQLDocs
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const IntegrationsGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('integrations')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('integrations') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['integrations', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const LocalDevelopmentGuidePage = async (props: { params: Promise<Params> }) =>
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('local-development')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('local-development') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['local-development', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const PlatformGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('platform')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('platform') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['platform', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const QueuesGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('queues')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('queues') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['queues', ...(params.slug ?? [])])
)
@@ -0,0 +1,15 @@
import SectionTroubleshootingPage, {
generateSectionTroubleshootingMetadata,
} from '~/features/docs/TroubleshootingSection.page'
export default async function RealtimeTroubleshootingPage() {
return (
<SectionTroubleshootingPage
topic="realtime"
sectionName="Realtime"
description="Search or browse troubleshooting guides for common realtime issues, including connection problems, subscription management, and message delivery."
/>
)
}
export const metadata = generateSectionTroubleshootingMetadata('realtime', 'Realtime')
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const ResourcesGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('resources')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('resources') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['resources', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const SecurityGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('security')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('security') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['security', ...(params.slug ?? [])])
)
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const SelfHostingGuidePage = async (props: { params: Promise<Params> }) => {
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('self-hosting')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('self-hosting') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['self-hosting', ...(params.slug ?? [])])
)
@@ -0,0 +1,15 @@
import SectionTroubleshootingPage, {
generateSectionTroubleshootingMetadata,
} from '~/features/docs/TroubleshootingSection.page'
export default async function StorageTroubleshootingPage() {
return (
<SectionTroubleshootingPage
topic="storage"
sectionName="Storage"
description="Search or browse troubleshooting guides for common storage issues, including file upload problems, bucket configuration, and security policies."
/>
)
}
export const metadata = generateSectionTroubleshootingMetadata('storage', 'Storage')
@@ -4,8 +4,8 @@ import {
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
import { IS_PROD } from 'common'
import { getEmptyArray } from '~/features/helpers.fn'
type Params = { slug?: string[] }
@@ -17,7 +17,7 @@ const MonitoringTroubleshootingGuidePage = async (props: { params: Promise<Param
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('telemetry')
const generateStaticParams = IS_PROD ? genGuidesStaticParams('telemetry') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['telemetry', ...(params.slug ?? [])])
)
@@ -653,7 +653,10 @@ export const auth = {
},
{
name: 'Debugging',
items: [{ name: 'Error Codes', url: '/guides/auth/debugging/error-codes' }],
items: [
{ name: 'Error Codes', url: '/guides/auth/debugging/error-codes' },
{ name: 'Troubleshooting', url: '/guides/auth/troubleshooting' },
],
},
{
name: 'Third-party auth',
@@ -962,6 +965,10 @@ export const database: NavMenuConstant = {
name: 'Supavisor',
url: '/guides/database/supavisor',
},
{
name: 'Troubleshooting',
url: '/guides/database/troubleshooting',
},
],
},
ormQuickstarts,
@@ -1630,7 +1637,10 @@ export const realtime: NavMenuConstant = {
{
name: 'Debugging',
url: undefined,
items: [{ name: 'Operational Error Codes', url: '/guides/realtime/error_codes', items: [] }],
items: [
{ name: 'Operational Error Codes', url: '/guides/realtime/error_codes', items: [] },
{ name: 'Troubleshooting', url: '/guides/realtime/troubleshooting' },
],
},
],
}
@@ -1752,6 +1762,7 @@ export const storage: NavMenuConstant = {
items: [
{ name: 'Logs', url: '/guides/storage/debugging/logs' },
{ name: 'Error Codes', url: '/guides/storage/debugging/error-codes' },
{ name: 'Troubleshooting', url: '/guides/storage/troubleshooting' },
],
},
{
@@ -77,6 +77,14 @@ SUPABASE_URL=your_supabase_project_url
SUPABASE_ANON_KEY=your_supabase_anon_key
```
</TabPanel>
<TabPanel id="react-router" label="React Router">
```bash .env
SUPABASE_URL=your_supabase_project_url
SUPABASE_ANON_KEY=your_supabase_anon_key
```
</TabPanel>
<TabPanel id="express" label="Express">
@@ -660,6 +668,105 @@ export default function Index() {
</TabPanel>
<TabPanel id="react-router" label="React Router">
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="react-router-loader"
queryGroup="environment"
>
<TabPanel id="react-router-loader" label="Loader">
```ts _index.tsx
import { LoaderFunctionArgs } from 'react-router'
import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr'
export async function loader({ request }: LoaderFunctionArgs) {
const headers = new Headers()
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
cookies: {
getAll() {
return parseCookieHeader(request.headers.get('Cookie') ?? '')
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
headers.append('Set-Cookie', serializeCookieHeader(name, value, options))
)
},
},
})
return new Response('...', {
headers,
})
}
```
</TabPanel>
<TabPanel id="react-router-action" label="Action">
```ts _index.tsx
import { type ActionFunctionArgs } from '@react-router'
import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr'
export async function action({ request }: ActionFunctionArgs) {
const headers = new Headers()
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
cookies: {
getAll() {
return parseCookieHeader(request.headers.get('Cookie') ?? '')
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
headers.append('Set-Cookie', serializeCookieHeader(name, value, options))
)
},
},
})
return new Response('...', {
headers,
})
}
```
</TabPanel>
<TabPanel id="react-router-component" label="Component">
```ts _index.tsx
import { type LoaderFunctionArgs } from "react-router";
import { useLoaderData } from "react-router";
import { createBrowserClient } from "@supabase/ssr";
export async function loader({}: LoaderFunctionArgs) {
return {
env: {
SUPABASE_URL: process.env.SUPABASE_URL!,
SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!,
},
};
}
export default function Index() {
const { env } = useLoaderData<typeof loader>();
const supabase = createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY);
return ...
}
```
</TabPanel>
</Tabs>
</TabPanel>
<TabPanel id="express" label="Express">
<Tabs
@@ -15,7 +15,7 @@ There are three general ways to use Sign in with Apple, depending on the applica
- Sign in on the web or in web-based apps
- Using an OAuth flow initiated by Supabase Auth using the [Sign in with Apple REST API](https://developer.apple.com/documentation/signinwithapplerestapi).
- Using [Sign in with Apple JS](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js) directly in the browser, usually suitable for websites.
- Using [Sign in with Apple JS](https://developer.apple.com/documentation/signinwithapplejs/) directly in the browser, usually suitable for websites.
- Sign in natively inside iOS, macOS, watchOS or tvOS apps using [Apple's Authentication Services](https://developer.apple.com/documentation/authenticationservices)
In some cases you're able to use the OAuth flow within web-based native apps such as with [React Native](https://reactnative.dev), [Expo](https://expo.dev) or other similar frameworks. It is best practice to use native Sign in with Apple capabilities on those platforms instead.
@@ -95,7 +95,7 @@ curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
## Using sign in with Apple JS
[Sign in with Apple JS](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js) is an official Apple framework for authenticating Apple users on websites. Although it can be used in web-based apps, those use cases will benefit more with the OAuth flow described above. We recommend using this method on classic websites only.
[Sign in with Apple JS](https://developer.apple.com/documentation/signinwithapplejs/) is an official Apple framework for authenticating Apple users on websites. Although it can be used in web-based apps, those use cases will benefit more with the OAuth flow described above. We recommend using this method on classic websites only.
You can use the `signInWithIdToken()` method from the Supabase JavaScript library on the website to obtain an access and refresh token once the user has given consent using Sign in with Apple JS:
@@ -48,18 +48,24 @@ show "statement_timeout";
Some settings can only be modified by a superuser. Supabase pre-enables the [`supautils` extension](https://supabase.com/blog/roles-postgres-hooks#setting-up-the-supautils-extension), which allows the `postgres` role to retain certain superuser privileges. It enables modification of the below reserved configurations at the `role` level:
| Setting | Description |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `auto_explain.log_min_duration` | Logs query plans taking longer than this duration. |
| `auto_explain.log_nested_statements` | Log nested statements' plans. |
| `log_min_messages` | Minimum severity level of messages to log. |
| `pg_net.ttl` | Sets how long the [pg_net extension](/docs/guides/database/extensions/pg_net) saves responses |
| `pg_net.batch_size` | Sets how many requests the [pg_net extension](/docs/guides/database/extensions/pg_net) can make per second |
| `pgaudit.*` | Configures the [PGAudit extension](https://supabase.com/docs/guides/database/extensions/pgaudit). The `log_parameter` is still restricted to protect secrets |
| `pgrst.*` | [`PostgREST` settings](https://docs.postgrest.org/en/stable/references/configuration.html#db-aggregates-enabled) |
| `plan_filter.*` | Configures the [pg_plan_filter extension](https://supabase.com/docs/guides/database/extensions/pg_plan_filter) |
| `session_replication_role` | Sets the session's behavior for triggers and rewrite rules. |
| `track_io_timing` | Collects timing statistics for database I/O activity. |
| Setting | Description |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auto_explain.*` | Configures the [auto_explain module](https://www.postgresql.org/docs/current/auto-explain.html). Can be configured to log execution plans for queries expected to exceed x seconds, including function queries. |
| `log_lock_waits` | Controls whether a log message is produced when a session waits longer than [deadlock_timeout](https://www.postgresql.org/docs/current/runtime-config-locks.html#GUC-DEADLOCK-TIMEOUT) to acquire a lock. |
| `log_min_duration_statement` | Causes the duration of each completed statement to be logged if the statement ran for at least the specified amount of time. |
| `log_min_messages` | Minimum severity level of messages to log. |
| `log_replication_commands` | Logs all replication commands |
| `log_statement` | Controls which SQL statements are logged. Valid values are `none` (off), `ddl`, `mod`, and `all` (all statements). |
| `log_temp_files` | Controls logging of temporary file names and sizes. |
| `pg_net.ttl` | Sets how long the [pg_net extension](/docs/guides/database/extensions/pg_net) saves responses |
| `pg_net.batch_size` | Sets how many requests the [pg_net extension](/docs/guides/database/extensions/pg_net) can make per second |
| `pg_stat_statements.*` | Configures the [pg_stat_statements extension](https://www.postgresql.org/docs/current/pgstatstatements.html). |
| `pgaudit.*` | Configures the [PGAudit extension](https://supabase.com/docs/guides/database/extensions/pgaudit). The `log_parameter` is still restricted to protect secrets |
| `pgrst.*` | [`PostgREST` settings](https://docs.postgrest.org/en/stable/references/configuration.html#db-aggregates-enabled) |
| `plan_filter.*` | Configures the [pg_plan_filter extension](https://supabase.com/docs/guides/database/extensions/pg_plan_filter) |
| `session_replication_role` | Sets the session's behavior for triggers and rewrite rules. |
| `track_io_timing` | Collects timing statistics for database I/O activity. |
| `wal_compression` | This parameter enables compression of WAL using the specified compression method. |
For example, to enable `log_nested_statements` for the `postgres` role, execute:
@@ -17,11 +17,11 @@ If you get stuck while working through this guide, refer to the [full example on
## Building the app
Let's start building the Svelte app from scratch.
Start building the Svelte app from scratch.
### Initialize a Svelte app
We can use the Vite Svelte TypeScript Template to initialize an app called `supabase-svelte`:
You can use the Vite Svelte TypeScript Template to initialize an app called `supabase-svelte`:
```bash
npm create vite@latest supabase-svelte -- --template svelte-ts
@@ -29,14 +29,14 @@ cd supabase-svelte
npm install
```
Then let's install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js)
Install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js)
```bash
npm install @supabase/supabase-js
```
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
Finally, save the environment variables in a `.env`.
All you need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
<$CodeTabs>
@@ -47,221 +47,46 @@ VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
</$CodeTabs>
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
Now you have the API credentials in place, create a helper file to initialize the Supabase client. These variables will be exposed on the browser, and that's fine since you have [Row Level Security](/docs/guides/auth#row-level-security) enabled on the Database.
<$CodeTabs>
```js name=src/supabaseClient.ts
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/supabaseClient.ts"
meta="name=src/supabaseClient.ts"
/>
### App styling (optional)
An optional step is to update the CSS file `src/app.css` to make the app look nice.
You can find the full contents of this file [here](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/svelte-user-management/src/app.css).
Optionally, update the CSS file `src/app.css` to make the app look nice.
You can find the full contents of this file [on GitHub](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/svelte-user-management/src/app.css).
### Set up a login component
Let's set up a Svelte component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
Set up a Svelte component to manage logins and sign ups. It uses Magic Links, so users can sign in with their email without using passwords.
<$CodeTabs>
```html name=src/lib/Auth.svelte
<script lang="ts">
import { supabase } from '../supabaseClient'
let loading = false
let email = ''
const handleLogin = async () => {
try {
loading = true
const { error } = await supabase.auth.signInWithOtp({ email })
if (error) throw error
alert('Check your email for login link!')
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
loading = false
}
}
</script>
<div class="row flex-center flex">
<div class="col-6 form-widget" aria-live="polite">
<h1 class="header">Supabase + Svelte</h1>
<p class="description">Sign in via magic link with your email below</p>
<form class="form-widget" on:submit|preventDefault="{handleLogin}">
<div>
<label for="email">Email</label>
<input
id="email"
class="inputField"
type="email"
placeholder="Your email"
bind:value="{email}"
/>
</div>
<div>
<button type="submit" class="button block" aria-live="polite" disabled="{loading}">
<span>{loading ? 'Loading' : 'Send magic link'}</span>
</button>
</div>
</form>
</div>
</div>
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/lib/Auth.svelte"
meta="name=src/lib/Auth.svelte"
/>
### Account page
After a user is signed in we can allow them to edit their profile details and manage their account.
Let's create a new component for that called `Account.svelte`.
After a user is signed in, allow them to edit their profile details and manage their account.
Create a new component for that called `Account.svelte`.
<$CodeTabs>
```tsx name=src/lib/Account.svelte
<script lang="ts">
import { onMount } from 'svelte'
import type { AuthSession } from '@supabase/supabase-js'
import { supabase } from '../supabaseClient'
export let session: AuthSession
let loading = false
let username: string | null = null
let website: string | null = null
let avatarUrl: string | null = null
onMount(() => {
getProfile()
})
const getProfile = async () => {
try {
loading = true
const { user } = session
const { data, error, status } = await supabase
.from('profiles')
.select('username, website, avatar_url')
.eq('id', user.id)
.single()
if (error && status !== 406) throw error
if (data) {
username = data.username
website = data.website
avatarUrl = data.avatar_url
}
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
loading = false
}
}
const updateProfile = async () => {
try {
loading = true
const { user } = session
const updates = {
id: user.id,
username,
website,
avatar_url: avatarUrl,
updated_at: new Date().toISOString(),
}
const { error } = await supabase.from('profiles').upsert(updates)
if (error) {
throw error
}
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
loading = false
}
}
</script>
<form on:submit|preventDefault="{updateProfile}" class="form-widget">
<div>Email: {session.user.email}</div>
<div>
<label for="username">Name</label>
<input id="username" type="text" bind:value="{username}" />
</div>
<div>
<label for="website">Website</label>
<input id="website" type="text" bind:value="{website}" />
</div>
<div>
<button type="submit" class="button primary block" disabled="{loading}">
{loading ? 'Saving ...' : 'Update profile'}
</button>
</div>
<button type="button" class="button block" on:click={() => supabase.auth.signOut()}> Sign Out
</button>
</form>
```
</$CodeTabs>
<$CodeSample
path="/user-management/svelte-user-management/src/lib/Account.svelte"
lines={[[1, 4], [7, 11], [14, 33], [34,53], [55,73], [75,-1]]}
meta="src/lib/Account.svelte"
/>
### Launch!
Now that we have all the components in place, let's update `App.svelte`:
Now that you have all the components in place, update `App.svelte`:
<$CodeTabs>
```html name=src/App.svelte
<script lang="ts">
import { onMount } from 'svelte'
import { supabase } from './supabaseClient'
import type { AuthSession } from '@supabase/supabase-js'
import Account from './lib/Account.svelte'
import Auth from './lib/Auth.svelte'
let session: AuthSession | null
onMount(() => {
supabase.auth.getSession().then(({ data }) => {
session = data.session
})
supabase.auth.onAuthStateChange((_event, _session) => {
session = _session
})
})
</script>
<div class="container" style="padding: 50px 0 100px 0">
{#if !session}
<Auth />
{:else}
<Account {session} />
{/if}
</div>
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/App.svelte"
meta="name=src/App.svelte"
/>
Once that's done, run this in a terminal window:
@@ -273,7 +98,7 @@ And then open the browser to [localhost:5173](http://localhost:5173) and you sho
<Admonition type="tip">
Svelte uses Vite and the default port is `5173`, Supabase uses `port 3000`. To change the redirection port for Supabase go to: `Authentication > Settings` and change the `Site Url` to `http://localhost:5173/`
Svelte uses Vite and the default port is `5173`, Supabase uses `port 3000`. To change the redirection port for Supabase go to: **Authentication > URL Configuration** and change the **Site URL** to `http://localhost:5173/`
</Admonition>
@@ -285,118 +110,21 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
### Create an upload widget
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
Create an avatar for the user so that they can upload a profile photo. Start by creating a new component:
<$CodeTabs>
```html name=src/lib/Avatar.svelte
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { supabase } from '../supabaseClient'
export let size: number
export let url: string | null = null
let avatarUrl: string | null = null
let uploading = false
let files: FileList
const dispatch = createEventDispatcher()
const downloadImage = async (path: string) => {
try {
const { data, error } = await supabase.storage.from('avatars').download(path)
if (error) {
throw error
}
const url = URL.createObjectURL(data)
avatarUrl = url
} catch (error) {
if (error instanceof Error) {
console.log('Error downloading image: ', error.message)
}
}
}
const uploadAvatar = async () => {
try {
uploading = true
if (!files || files.length === 0) {
throw new Error('You must select an image to upload.')
}
const file = files[0]
const fileExt = file.name.split('.').pop()
const filePath = `${Math.random()}.${fileExt}`
const { error } = await supabase.storage.from('avatars').upload(filePath, file)
if (error) {
throw error
}
url = filePath
dispatch('upload')
} catch (error) {
if (error instanceof Error) {
alert(error.message)
}
} finally {
uploading = false
}
}
$: if (url) downloadImage(url)
</script>
<div style="width: {size}px" aria-live="polite">
{#if avatarUrl} <img src={avatarUrl} alt={avatarUrl ? 'Avatar' : 'No image'} class="avatar image"
style="height: {size}px, width: {size}px" /> {:else}
<div class="avatar no-image" style="height: {size}px, width: {size}px" />
{/if}
<div style="width: {size}px">
<label class="button primary block" for="single">
{uploading ? 'Uploading ...' : 'Upload avatar'}
</label>
<span style="display:none">
<input
type="file"
id="single"
accept="image/*"
bind:files
on:change={uploadAvatar}
disabled={uploading}
/>
</span>
</div>
</div>
```
</$CodeTabs>
<$CodeSample
path="user-management/svelte-user-management/src/lib/Avatar.svelte"
meta="name=src/lib/Avatar.svelte"
/>
### Add the new widget
And then we can add the widget to the Account page:
And then you can add the widget to the Account page:
<$CodeTabs>
```html name=src/lib/Account.svelte
<script lang="ts">
// Import the new component
import Avatar from './Avatar.svelte'
</script>
<form on:submit|preventDefault="{updateProfile}" class="form-widget">
<!-- Add to body -->
<Avatar bind:url="{avatarUrl}" size="{150}" on:upload="{updateProfile}" />
<!-- Other form elements -->
</form>
```
</$CodeTabs>
<$CodeSample
path="/user-management/svelte-user-management/src/lib/Account.svelte"
lines={[[1,1], [5,5], [71,73], [74,74], [92,-1]]}
meta="src/lib/Account.svelte"
/>
At this stage you have a fully functional application!
@@ -193,7 +193,7 @@ Once you've chosen an available subdomain and have done all the necessary prepar
Use the [`vanity-subdomains activate`](/docs/reference/cli/supabase-vanity-subdomains-activate) command to activate and claim your subdomain:
```bash
supabase vanity-subdomains --project-ref abcdefghijklmnopqrst activate --desired-subdomain my-example-brand
supabase vanity-subdomains --project-ref abcdefghijklmnopqrst activate --desired-subdomain my-example-brand --experimental
```
If you wish to use the new domain in client code, you can set it up like so:
@@ -21,6 +21,12 @@ Is the server running on that host and accepting TCP/IP connections?`, this coul
These bans will clear after 30mins but you can unban the IPs using the Supabase CLI https://supabase.com/docs/guides/cli following the commands below.
## Unban IPs using the Dashboard
Go to [Database Settings](https://supabase.com/dashboard/project/_/database/settings#banned-ips) and click **Unban IP**.
## Unban IPs using the CLI
How to list the banned IPs:
```
@@ -344,6 +344,8 @@ function matchLang(lang: string) {
return 'swift'
case 'sql':
return 'sql'
case 'svelte':
return 'svelte'
default:
return null
}
@@ -4,6 +4,7 @@ import { ChevronDown, RotateCw, Search, X } from 'lucide-react'
import { useQueryStates } from 'nuqs'
import { useEffect, useRef, useState, Suspense, useCallback, useMemo } from 'react'
import { useBreakpoint } from 'common'
import {
Input_Shadcn_,
cn,
@@ -13,22 +14,13 @@ import {
CollapsibleContent_Shadcn_ as CollapsibleContent,
} from 'ui'
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
import {
MultiSelector,
MultiSelectorContent,
MultiSelectorInput,
MultiSelectorItem,
MultiSelectorList,
MultiSelectorTrigger,
} from 'ui-patterns/multi-select'
import { MultiSelector } from 'ui-patterns/multi-select'
import { type ITroubleshootingMetadata } from './Troubleshooting.utils'
import {
TROUBLESHOOTING_CONTAINER_ID,
TROUBLESHOOTING_DATA_ATTRIBUTES,
troubleshootingSearchParams,
} from './Troubleshooting.utils.shared'
import { useBreakpoint } from 'common'
function useTroubleshootingSearchState() {
const [_state, _setState] = useQueryStates(troubleshootingSearchParams)
@@ -115,10 +107,10 @@ function entryMatchesFilter(
}
interface TroubleshootingFilterProps {
products: string[]
className?: string
products?: string[]
errors: ITroubleshootingMetadata['errors']
keywords: string[]
className?: string
}
export function TroubleshootingFilter(props: TroubleshootingFilterProps) {
@@ -223,18 +215,20 @@ function TroubleshootingFilterInternal({
<>
<h2 className="sr-only">Search and filter</h2>
<div className={cn('flex flex-wrap gap-2 items-center', className)}>
<MultiSelector values={selectedProducts} onValuesChange={setSelectedProducts}>
<MultiSelector.Trigger badgeLimit={1} className="w-48" label="Products" />
<MultiSelector.Content>
<MultiSelector.List>
{products?.map((product) => (
<MultiSelector.Item key={`product-${product}`} value={product}>
{product}
</MultiSelector.Item>
))}
</MultiSelector.List>
</MultiSelector.Content>
</MultiSelector>
{!!products && (
<MultiSelector values={selectedProducts} onValuesChange={setSelectedProducts}>
<MultiSelector.Trigger badgeLimit={1} className="w-48" label="Products" />
<MultiSelector.Content>
<MultiSelector.List>
{products?.map((product) => (
<MultiSelector.Item key={`product-${product}`} value={product}>
{product}
</MultiSelector.Item>
))}
</MultiSelector.List>
</MultiSelector.Content>
</MultiSelector>
)}
<MultiSelector values={selectedErrorCodes} onValuesChange={setSelectedErrorCodes}>
<MultiSelector.Trigger badgeLimit={1} className="w-48" label="Error codes" />
<MultiSelector.Content>
+74 -8
View File
@@ -1,15 +1,19 @@
import { Wrench } from 'lucide-react'
import Link from 'next/link'
import { type PropsWithChildren, useCallback } from 'react'
import { Fragment } from 'react'
import { cn } from 'ui'
import { TroubleshootingFilter } from './Troubleshooting.ui.client'
import {
type ITroubleshootingEntry,
type ITroubleshootingMetadata,
getArticleSlug,
getTroubleshootingUpdatedDates,
} from './Troubleshooting.utils'
import { TroubleshootingFilter } from './Troubleshooting.ui.client'
import { formatError, TROUBLESHOOTING_DATA_ATTRIBUTES } from './Troubleshooting.utils.shared'
import { cn } from 'ui'
import {
formatError,
TROUBLESHOOTING_DATA_ATTRIBUTES,
TROUBLESHOOTING_CONTAINER_ID,
} from './Troubleshooting.utils.shared'
export async function TroubleshootingPreview({ entry }: { entry: ITroubleshootingEntry }) {
const dateUpdated = entry.data.database_id.startsWith('pseudo-')
@@ -55,10 +59,10 @@ export async function TroubleshootingPreview({ entry }: { entry: ITroubleshootin
.map(formatError)
.filter(Boolean)
.map((error, index) => (
<>
<code key={index}>{error}</code>
<Fragment key={error}>
<code>{error}</code>
{index < (entry.data.errors?.length || 0) - 1 ? ', ' : ''}
</>
</Fragment>
))}
</span>
)}
@@ -83,3 +87,65 @@ export async function TroubleshootingPreview({ entry }: { entry: ITroubleshootin
</div>
)
}
export function TroubleshootingHeader({
title,
description,
keywords,
products,
errors,
}: {
title: string
description: string
keywords: Array<string>
products?: Array<string>
errors: ITroubleshootingMetadata['errors']
}) {
return (
<div className="lg:sticky lg:top-[var(--header-height)] lg:z-10 bg-background">
<div className="pt-8 pb-6 px-5">
<h1 className="text-4xl tracking-tight mb-7">{title}</h1>
<p className="text-lg text-foreground-light">{description}</p>
<hr className="my-7" aria-hidden />
<TroubleshootingFilter
keywords={keywords}
products={products}
errors={errors}
className="mb-0"
/>
</div>
</div>
)
}
export function TroubleshootingEntries({
name,
entries,
}: {
name: string
entries: Array<ITroubleshootingEntry>
}) {
return (
<div id={TROUBLESHOOTING_CONTAINER_ID} className="@container/troubleshooting">
<h2 className="sr-only">Matching troubleshooting entries</h2>
{entries.length === 0 ? (
<div className="text-center py-12">
<p className="text-foreground-light text-lg">
No troubleshooting guides available for {name} yet.
</p>
</div>
) : (
<ul className="grid @4xl/troubleshooting:grid-cols-[78%_15%_7%]">
{entries.map((entry) => (
<li
key={entry.data.database_id}
className="grid grid-cols-subgrid @4xl/troubleshooting:col-span-3"
>
<TroubleshootingPreview entry={entry} />
</li>
))}
</ul>
)}
</div>
)
}
@@ -113,3 +113,60 @@ async function getTroubleshootingUpdatedDatesInternal() {
}, new Map<string, Date>())
}
export const getTroubleshootingUpdatedDates = cache(getTroubleshootingUpdatedDatesInternal)
async function getTroubleshootingEntriesByTopicImpl(
topic: ITroubleshootingMetadata['topics'][number]
) {
const allEntries = await getAllTroubleshootingEntries()
return allEntries.filter((entry) => entry.data.topics.includes(topic))
}
export const getTroubleshootingEntriesByTopic = cache_fullProcess_withDevCacheBust(
getTroubleshootingEntriesByTopicImpl,
TROUBLESHOOTING_DIRECTORY,
() => JSON.stringify([])
)
export async function getTroubleshootingKeywordsByTopic(
topic: ITroubleshootingMetadata['topics'][number]
) {
const entries = await getTroubleshootingEntriesByTopic(topic)
const keywords = new Set<string>()
for (const entry of entries) {
for (const entryTopic of entry.data.topics) {
keywords.add(entryTopic)
}
for (const keyword of entry.data.keywords ?? []) {
keywords.add(keyword)
}
}
return Array.from(keywords).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
}
export async function getTroubleshootingErrorsByTopic(
topic: ITroubleshootingMetadata['topics'][number]
) {
const entries = await getTroubleshootingEntriesByTopic(topic)
const allErrors = new Set(
entries
.flatMap((entry) => entry.data.errors ?? [])
.filter((error) => error?.http_status_code || error?.code)
)
const seen = new Set<string>()
for (const error of allErrors) {
const key = formatError(error)
if (seen.has(key)) {
allErrors.delete(error)
}
seen.add(key)
}
function sortErrors(
a: NonNullable<ITroubleshootingMetadata['errors']>[number],
b: NonNullable<ITroubleshootingMetadata['errors']>[number]
) {
return formatError(a).localeCompare(formatError(b))
}
return Array.from(allErrors).sort(sortErrors)
}
@@ -0,0 +1,68 @@
import { type Metadata } from 'next'
import { TroubleshootingHeader, TroubleshootingEntries } from '~/features/docs/Troubleshooting.ui'
import {
TroubleshootingFilterEmptyState,
TroubleshootingListController,
} from '~/features/docs/Troubleshooting.ui.client'
import {
type ITroubleshootingMetadata,
getTroubleshootingEntriesByTopic,
getTroubleshootingErrorsByTopic,
getTroubleshootingKeywordsByTopic,
} from '~/features/docs/Troubleshooting.utils'
import { PROD_URL } from '~/lib/constants'
interface SectionTroubleshootingPageProps {
topic: ITroubleshootingMetadata['topics'][number]
sectionName: string
description?: string
}
export default async function SectionTroubleshootingPage({
topic,
sectionName,
description,
}: SectionTroubleshootingPageProps) {
// All other fetches are dependent on getTroubleshootingEntriesByTopic, which
// is cached, so it's run first to populate the cache
const troubleshootingEntries = await getTroubleshootingEntriesByTopic(topic)
const [keywords, errors] = await Promise.all([
getTroubleshootingKeywordsByTopic(topic),
getTroubleshootingErrorsByTopic(topic),
])
const pageTitle = `${sectionName} Troubleshooting`
const pageDescription =
description || `Search or browse troubleshooting guides for common ${sectionName} issues.`
return (
<div className="flex flex-col">
<TroubleshootingHeader
title={pageTitle}
description={pageDescription}
keywords={keywords}
errors={errors}
/>
<div className="flex-1">
<div className="px-5 pt-6">
<TroubleshootingListController />
<TroubleshootingFilterEmptyState />
<TroubleshootingEntries name={sectionName} entries={troubleshootingEntries} />
</div>
</div>
</div>
)
}
export function generateSectionTroubleshootingMetadata(
topic: string,
sectionName: string
): Metadata {
return {
title: `Supabase Docs | ${sectionName} Troubleshooting`,
alternates: {
canonical: `${PROD_URL}/guides/${topic}/troubleshooting`,
},
}
}
+1
View File
@@ -46,6 +46,7 @@ Etienne Stalmans
Fabrizio Fenoglio
Felipe Stival
Francesco Sansalvadore
Greg Kress
Greg P
Greg Richardson
Guilherme Souza
+2 -3
View File
@@ -5,7 +5,6 @@ import { HTML5Backend } from 'react-dnd-html5-backend'
import { createPortal } from 'react-dom'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { RoleImpersonationState } from 'lib/role-impersonation'
import { EMPTY_ARR } from 'lib/void'
@@ -20,6 +19,7 @@ import Header, { HeaderProps } from './components/header/Header'
import { RowContextMenu } from './components/menu'
import { GridProps } from './types'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useTableFilter } from './hooks/useTableFilter'
import { useTableSort } from './hooks/useTableSort'
@@ -35,8 +35,7 @@ export const SupabaseGrid = ({
const { id: _id } = useParams()
const tableId = _id ? Number(_id) : undefined
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
const snap = useTableEditorTableStateSnapshot()
@@ -4,10 +4,11 @@ import type { RenderEditCellProps } from 'react-data-grid'
import { toast } from 'sonner'
import { useParams } from 'common'
import { isValueTruncated } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTableLike } from 'data/table-editor/table-editor-types'
import { useGetCellValueMutation } from 'data/table-rows/get-cell-value-mutation'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { prettifyJSON, removeJSONTrailingComma, tryParseJson } from 'lib/helpers'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
import { Popover, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
@@ -15,7 +16,6 @@ import { BlockKeys } from '../common/BlockKeys'
import { MonacoEditor } from '../common/MonacoEditor'
import { NullValue } from '../common/NullValue'
import { TruncatedWarningOverlay } from './TruncatedWarningOverlay'
import { isValueTruncated } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
const verifyJSON = (value: string) => {
try {
@@ -52,10 +52,10 @@ export const JsonEditor = <TRow, TSummaryRow = unknown>({
onRowChange,
onExpandEditor,
}: JsonEditorProps<TRow, TSummaryRow>) => {
const snap = useTableEditorTableStateSnapshot()
const { id: _id } = useParams()
const id = _id ? Number(_id) : undefined
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const snap = useTableEditorTableStateSnapshot()
const { data: selectedTable } = useTableEditorQuery({
projectRef: project?.ref,
@@ -4,10 +4,11 @@ import type { RenderEditCellProps } from 'react-data-grid'
import { toast } from 'sonner'
import { useParams } from 'common'
import { isValueTruncated } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTableLike } from 'data/table-editor/table-editor-types'
import { useGetCellValueMutation } from 'data/table-rows/get-cell-value-mutation'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
import { Button, Popover, Tooltip, TooltipContent, TooltipTrigger, cn } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
@@ -16,7 +17,6 @@ import { EmptyValue } from '../common/EmptyValue'
import { MonacoEditor } from '../common/MonacoEditor'
import { NullValue } from '../common/NullValue'
import { TruncatedWarningOverlay } from './TruncatedWarningOverlay'
import { isValueTruncated } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
export const TextEditor = <TRow, TSummaryRow = unknown>({
row,
@@ -33,7 +33,7 @@ export const TextEditor = <TRow, TSummaryRow = unknown>({
const snap = useTableEditorTableStateSnapshot()
const { id: _id } = useParams()
const id = _id ? Number(_id) : undefined
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const { data: selectedTable } = useTableEditorQuery({
projectRef: project?.ref,
@@ -1,9 +1,9 @@
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { GridFooter } from 'components/ui/GridFooter'
import TwoOptionToggle from 'components/ui/TwoOptionToggle'
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTableLike, isViewLike } from 'data/table-editor/table-editor-types'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useUrlState } from 'hooks/ui/useUrlState'
import RefreshButton from '../header/RefreshButton'
import { Pagination } from './pagination'
@@ -13,9 +13,9 @@ export interface FooterProps {
}
const Footer = ({ isRefetching }: FooterProps) => {
const { project } = useProjectContext()
const { id: _id } = useParams()
const id = _id ? Number(_id) : undefined
const { data: project } = useSelectedProjectQuery()
const { data: entity } = useTableEditorQuery({
projectRef: project?.ref,
@@ -3,10 +3,10 @@ import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { useTableFilter } from 'components/grid/hooks/useTableFilter'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTable } from 'data/table-editor/table-editor-types'
import { THRESHOLD_COUNT, useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { RoleImpersonationState } from 'lib/role-impersonation'
import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state'
import { useTableEditorStateSnapshot } from 'state/table-editor'
@@ -27,7 +27,7 @@ const Pagination = () => {
const { id: _id } = useParams()
const id = _id ? Number(_id) : undefined
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
const snap = useTableEditorTableStateSnapshot()
@@ -3,11 +3,11 @@ import type { PropsWithChildren } from 'react'
import type { RenderCellProps } from 'react-data-grid'
import { convertByteaToHex } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useTableEditorQuery } from 'data/table-editor/table-editor-query'
import { isTableLike } from 'data/table-editor/table-editor-types'
import { useTablesQuery } from 'data/tables/tables-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
import type { SupaRow } from '../../types'
import { NullValue } from '../common/NullValue'
@@ -18,9 +18,8 @@ interface Props extends PropsWithChildren<RenderCellProps<SupaRow, unknown>> {
}
export const ForeignKeyFormatter = (props: Props) => {
const { project } = useProjectContext()
const { tableId, row, column } = props
const { data: project } = useSelectedProjectQuery()
const { data } = useTableEditorQuery({
projectRef: project?.ref,
@@ -11,7 +11,7 @@ import {
import { convertByteaToHex } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id'
import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Button, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
@@ -23,7 +23,7 @@ interface ReferenceRecordPeekProps {
export const ReferenceRecordPeek = ({ table, column, value }: ReferenceRecordPeekProps) => {
const { ref } = useParams()
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const { data, error, isSuccess, isError, isLoading } = useTableRowsQuery(
{
@@ -3,12 +3,12 @@ import DataGrid, { CalculatedColumn, DataGridHandle } from 'react-data-grid'
import { handleCopyCell } from 'components/grid/SupabaseGrid.utils'
import { formatForeignKeys } from 'components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
import { Button, cn } from 'ui'
@@ -54,6 +54,9 @@ export const Grid = memo(
const tableEditorSnap = useTableEditorStateSnapshot()
const snap = useTableEditorTableStateSnapshot()
const { data: org } = useSelectedOrganizationQuery()
const { data: project } = useSelectedProjectQuery()
const onRowsChange = useOnRowsChange(rows)
function onSelectedRowsChange(selectedRows: Set<number>) {
@@ -71,8 +74,6 @@ export const Grid = memo(
const tableEntityType = snap.originalTable?.entity_type
const { mutate: sendEvent } = useSendEventMutation()
const org = useSelectedOrganization()
const { project } = useProjectContext()
const { data } = useForeignKeyConstraintsQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
@@ -5,20 +5,20 @@ import { toast } from 'sonner'
import { SupaRow } from 'components/grid/types'
import { convertByteaToHex } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import { isTableLike } from 'data/table-editor/table-editor-types'
import { tableRowKeys } from 'data/table-rows/keys'
import { useTableRowUpdateMutation } from 'data/table-rows/table-row-update-mutation'
import type { TableRowsData } from 'data/table-rows/table-rows-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useGetImpersonatedRoleState } from 'state/role-impersonation-state'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
import { Dictionary } from 'types'
export function useOnRowsChange(rows: SupaRow[]) {
const { project } = useProjectContext()
const snap = useTableEditorTableStateSnapshot()
const queryClient = useQueryClient()
const { data: project } = useSelectedProjectQuery()
const snap = useTableEditorTableStateSnapshot()
const getImpersonatedRoleState = useGetImpersonatedRoleState()
const { mutate: mutateUpdateTableRow } = useTableRowUpdateMutation({
@@ -1,8 +1,13 @@
import { useState } from 'react'
import { useParams } from 'common'
import { Filter, Sort, SupaTable } from 'components/grid/types'
import { getConnectionStrings } from 'components/interfaces/Connect/DatabaseSettings.utils'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { getAllTableRowsSql } from 'data/table-rows/table-rows-query'
import { pluckObjectFields } from 'lib/helpers'
import { useState } from 'react'
import { RoleImpersonationState, wrapWithRoleImpersonation } from 'lib/role-impersonation'
import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state'
import {
Button,
cn,
@@ -22,13 +27,25 @@ import {
import { Admonition } from 'ui-patterns'
interface ExportDialogProps {
table?: { name: string; schema: string }
table?: SupaTable
filters?: Filter[]
sorts?: Sort[]
ignoreRoleImpersonation?: boolean
open: boolean
onOpenChange: (open: boolean) => void
}
export const ExportDialog = ({ table, open, onOpenChange }: ExportDialogProps) => {
export const ExportDialog = ({
table,
filters = [],
sorts = [],
ignoreRoleImpersonation = false,
open,
onOpenChange,
}: ExportDialogProps) => {
const { ref: projectRef } = useParams()
const roleImpersonationState = useRoleImpersonationStateSnapshot()
const [selectedTab, setSelectedTab] = useState<string>('csv')
@@ -48,10 +65,18 @@ export const ExportDialog = ({ table, open, onOpenChange }: ExportDialogProps) =
})
const outputName = `${table?.name}_rows`
const queryChains = !table ? undefined : getAllTableRowsSql({ table, sorts, filters })
const query = !!queryChains
? ignoreRoleImpersonation
? queryChains.toSql()
: wrapWithRoleImpersonation(
queryChains.toSql(),
roleImpersonationState as RoleImpersonationState
)
: ''
const csvExportCommand = `
${connectionStrings.direct.psql} -c "COPY (SELECT * FROM "${table?.schema}"."${table?.name}") TO STDOUT WITH CSV HEADER DELIMITER ',';" > ${outputName}.csv
`.trim()
${connectionStrings.direct.psql} -c "COPY (${query}) TO STDOUT WITH CSV HEADER DELIMITER ',';" > ${outputName}.csv`.trim()
const sqlExportCommand = `
pg_dump -h ${db_host} -p ${db_port} -d ${db_name} -U ${db_user} --table="${table?.schema}.${table?.name}" --data-only --column-inserts > ${outputName}.sql
@@ -95,6 +120,12 @@ pg_dump -h ${db_host} -p ${db_port} -d ${db_name} -U ${db_user} --table="${table
value={sqlExportCommand}
className="[&_code]:text-[12px] [&_code]:text-foreground"
/>
<Admonition
type="note"
className="mt-2"
title="Filters are not supported when exporting as SQL via pg_dump"
description="If you'd like to export as SQL, we recommend creating a view first then exporting the data from there via pg_dump instead"
/>
</TabsContent_Shadcn_>
</Tabs_Shadcn_>
@@ -107,15 +138,11 @@ pg_dump -h ${db_host} -p ${db_port} -d ${db_name} -U ${db_user} --table="${table
</p>
{selectedTab === 'sql' && (
<Admonition
type="note"
title="The pg_dump version needs to match your Postgres version"
>
<p className="!leading-normal">
If you run into a server version mismatch error, you will need to update{' '}
<code>pg_dump</code> before running the command.
</p>
</Admonition>
<p className="text-sm text-foreground-light">
Note: <code>pg_dump</code> needs to match your project's Postgres version. If you run
into a server version mismatch error, you will need to update <code>pg_dump</code>{' '}
before running the command.
</p>
)}
</DialogSection>
<DialogFooter>
@@ -11,13 +11,13 @@ import { useTableFilter } from 'components/grid/hooks/useTableFilter'
import { useTableSort } from 'components/grid/hooks/useTableSort'
import GridHeaderActions from 'components/interfaces/TableGridEditor/GridHeaderActions'
import { formatTableRowsToSQL } from 'components/interfaces/TableGridEditor/TableEntity.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query'
import { fetchAllTableRows, useTableRowsQuery } from 'data/table-rows/table-rows-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { RoleImpersonationState } from 'lib/role-impersonation'
import {
useRoleImpersonationStateSnapshot,
@@ -222,7 +222,7 @@ const DefaultHeader = () => {
}
const RowHeader = () => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
const snap = useTableEditorTableStateSnapshot()
@@ -504,11 +504,9 @@ const RowHeader = () => {
Export
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className={snap.allRowsSelected ? 'w-52' : 'w-40'}>
<DropdownMenuContent align="start" className={snap.allRowsSelected ? 'w-52' : 'w-40'}>
<DropdownMenuItem onClick={onRowsExportCSV}>Export as CSV</DropdownMenuItem>
<DropdownMenuItem onClick={onRowsExportSQL}>Export as SQL</DropdownMenuItem>
{/* [Joshen] Should make this available for all cases, but that'll involve updating
the Dialog's SQL output to be dynamic based on any filters applied */}
{snap.allRowsSelected ? (
<DropdownMenuItem className="group" onClick={() => setShowExportModal(true)}>
<div>
@@ -535,7 +533,9 @@ const RowHeader = () => {
</div>
<ExportDialog
table={{ name: snap.table.name, schema: snap.table.schema ?? '' }}
table={snap.table}
filters={filters}
sorts={sorts}
open={showExportModal}
onOpenChange={() => setShowExportModal(false)}
/>
@@ -1,14 +1,14 @@
import { useCallback } from 'react'
import { saveTableEditorStateToLocalStorage } from 'components/grid/SupabaseGrid.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useTableEditorTableStateSnapshot } from 'state/table-editor-table'
/**
* Hook for saving state and triggering side effects.
*/
export function useSaveTableEditorState() {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const snap = useTableEditorTableStateSnapshot()
const saveDataAndTriggerSideEffects = useCallback(
@@ -9,7 +9,7 @@ import { DocsButton } from 'components/ui/DocsButton'
import { useLintRuleDeleteMutation } from 'data/lint/delete-lint-rule-mutation'
import { useProjectLintRulesQuery } from 'data/lint/lint-rules-query'
import { useOrganizationMembersQuery } from 'data/organizations/organization-members-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import {
Badge,
Button,
@@ -40,7 +40,7 @@ const SIMPLIFIED_INTERFACE = true
export const AdvisorRuleItem = ({ lint }: AdvisorRuleItemProps) => {
const { ref: projectRef } = useParams()
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const [open, setOpen] = useState(false)
const [expandedLint, setExpandedLint] = useState<string>()
@@ -8,7 +8,7 @@ import * as z from 'zod'
import { useParams } from 'common'
import { useLintRuleCreateMutation } from 'data/lint/create-lint-rule-mutation'
import { useOrganizationMembersQuery } from 'data/organizations/organization-members-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useRouter } from 'next/router'
import {
Button,
@@ -68,10 +68,9 @@ const defaultValues = {
export const CreateRuleSheet = ({ lint, open, onOpenChange }: CreateRuleSheetProps) => {
const router = useRouter()
const { ref: projectRef } = useParams()
// const [_, setExpandedLint] = useQueryState('lint')
const routeCategory = router.pathname.split('/').pop()
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const { data: members = [] } = useOrganizationMembersQuery({ slug: organization?.slug })
const { mutate: createRule, isLoading: isCreating } = useLintRuleCreateMutation({
@@ -1,7 +1,7 @@
import { Link } from 'lucide-react'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Badge, copyToClipboard } from 'ui'
import { useRegisterCommands, useSetCommandMenuOpen } from 'ui-patterns/CommandMenu'
import { COMMAND_MENU_SECTIONS } from './CommandMenu.utils'
@@ -10,7 +10,7 @@ import { orderCommandSectionsByPriority } from './ordering'
export function useApiUrlCommand() {
const setIsOpen = useSetCommandMenuOpen()
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const { data: settings } = useProjectSettingsV2Query(
{ projectRef: project?.ref },
{ enabled: !!project }
@@ -11,7 +11,7 @@ import {
} from 'react'
import { FeatureFlagContext, LOCAL_STORAGE_KEYS } from 'common'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useFlag, useIsRealtimeSettingsFFEnabled } from 'hooks/ui/useFlag'
import { EMPTY_OBJ } from 'lib/void'
import { FEATURE_PREVIEWS } from './FeaturePreview.constants'
@@ -92,7 +92,7 @@ export const useIsInlineEditorEnabled = () => {
}
export const useUnifiedLogsPreview = () => {
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const { flags, onUpdateFlag } = useFeaturePreviewContext()
const isTeamsOrEnterprise = ['team', 'enterprise'].includes(organization?.plan.id ?? '')
@@ -4,7 +4,7 @@ import { ReactNode } from 'react'
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
import { Badge, Button, Modal, ScrollArea, cn } from 'ui'
import { AdvisorRulesPreview } from './AdvisorRulesPreview'
@@ -38,7 +38,7 @@ const FeaturePreviewModal = () => {
closeFeaturePreviewModal,
isFeaturePreviewReleasedToPublic,
} = useFeaturePreviewModal()
const org = useSelectedOrganization()
const { data: org } = useSelectedOrganizationQuery()
const featurePreviewContext = useFeaturePreviewContext()
const { mutate: sendEvent } = useSendEventMutation()
@@ -7,7 +7,7 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import useLatest from 'hooks/misc/useLatest'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
@@ -20,7 +20,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
const snap = useAppStateSnapshot()
const isUserMFAEnabled = useIsMFAEnabled()
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const [dashboardHistory, _, { isSuccess: isSuccessStorage }] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.DASHBOARD_HISTORY(ref ?? ''),
@@ -7,12 +7,13 @@ import * as z from 'zod'
import { useParams } from 'common'
import { ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
import { StringNumberOrNull } from 'components/ui/Forms/Form.constants'
import NoPermission from 'components/ui/NoPermission'
import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
import {
AlertDescription_Shadcn_,
@@ -30,7 +31,6 @@ import {
WarningIcon,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { StringNumberOrNull } from 'components/ui/Forms/Form.constants'
const FormSchema = z.object({
API_MAX_REQUEST_DURATION: z.coerce
@@ -42,7 +42,7 @@ const FormSchema = z.object({
export const AdvancedAuthSettingsForm = () => {
const { ref: projectRef } = useParams()
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const canReadConfig = useCheckPermissions(PermissionAction.READ, 'custom_config_gotrue')
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
@@ -5,7 +5,7 @@ import { useParams } from 'common'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import {
Button,
DropdownMenu,
@@ -30,7 +30,7 @@ export const AddHookDropdown = ({
onSelectHook,
}: AddHookDropdownProps) => {
const { ref: projectRef } = useParams()
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const { data: authConfig } = useAuthConfigQuery({ projectRef })
const canUpdateAuthHook = useCheckPermissions(PermissionAction.AUTH_EXECUTE, '*')
@@ -9,13 +9,14 @@ import * as z from 'zod'
import { useParams } from 'common'
import { convertArgumentTypes } from 'components/interfaces/Database/Functions/Functions.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import CodeEditor from 'components/ui/CodeEditor/CodeEditor'
import { DocsButton } from 'components/ui/DocsButton'
import FunctionSelector from 'components/ui/FunctionSelector'
import SchemaSelector from 'components/ui/SchemaSelector'
import { AuthConfigResponse } from 'data/auth/auth-config-query'
import { useAuthHooksUpdateMutation } from 'data/auth/auth-hooks-update-mutation'
import { executeSql } from 'data/sql/execute-sql-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
Button,
FormControl_Shadcn_,
@@ -38,7 +39,6 @@ import {
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { HOOKS_DEFINITIONS, HOOK_DEFINITION_TITLE, Hook } from './hooks.constants'
import { extractMethod, getRevokePermissionStatements, isValidHook } from './hooks.utils'
import { DocsButton } from 'components/ui/DocsButton'
interface CreateHookSheetProps {
visible: boolean
@@ -115,7 +115,7 @@ export const CreateHookSheet = ({
onDelete,
}: CreateHookSheetProps) => {
const { ref: projectRef } = useParams()
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const definition = useMemo(
() => HOOKS_DEFINITIONS.find((d) => d.title === title) || HOOKS_DEFINITIONS[0],
@@ -2,13 +2,13 @@ import { useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import CodeEditor from 'components/ui/CodeEditor/CodeEditor'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthHooksUpdateMutation } from 'data/auth/auth-hooks-update-mutation'
import { executeSql } from 'data/sql/execute-sql-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { cn } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { AddHookDropdown } from './AddHookDropdown'
@@ -19,7 +19,7 @@ import { extractMethod, getRevokePermissionStatements, isValidHook } from './hoo
export const HooksListing = () => {
const { ref: projectRef } = useParams()
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { data: authConfig, error: authConfigError, isError } = useAuthConfigQuery({ projectRef })
const [selectedHook, setSelectedHook] = useState<HOOK_DEFINITION_TITLE | null>(null)
@@ -12,7 +12,7 @@ import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
import {
AlertDescription_Shadcn_,
@@ -26,13 +26,13 @@ import {
FormField_Shadcn_,
Form_Shadcn_,
Input_Shadcn_,
Select_Shadcn_,
PrePostTab,
SelectContent_Shadcn_,
SelectItem_Shadcn_,
SelectTrigger_Shadcn_,
SelectValue_Shadcn_,
Select_Shadcn_,
WarningIcon,
PrePostTab,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
@@ -90,7 +90,7 @@ const MfaAuthSettingsForm = () => {
const canReadConfig = useCheckPermissions(PermissionAction.READ, 'custom_config_gotrue')
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const isProPlanAndUp = organization?.plan?.id !== 'free'
const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
@@ -11,12 +11,12 @@ import {
PolicyTableRowProps,
} from 'components/interfaces/Auth/Policies/PolicyTableRow'
import { ProtectedSchemaWarning } from 'components/interfaces/Database/ProtectedSchemaWarning'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import InformationBox from 'components/ui/InformationBox'
import { useDatabasePolicyDeleteMutation } from 'data/database-policies/database-policy-delete-mutation'
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
interface PoliciesProps {
@@ -38,7 +38,7 @@ const Policies = ({
}: PoliciesProps) => {
const router = useRouter()
const { ref } = useParams()
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const [selectedTableToToggleRLS, setSelectedTableToToggleRLS] = useState<{
id: number
@@ -1,11 +1,11 @@
import { SYSTEM_ROLES } from 'components/interfaces/Database/Roles/Roles.constants'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import MultiSelect from 'ui-patterns/MultiSelectDeprecated'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useDatabaseRolesQuery } from 'data/database-roles/database-roles-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { sortBy } from 'lodash'
import MultiSelect from 'ui-patterns/MultiSelectDeprecated'
interface PolicyRolesProps {
selectedRoles: string[]
@@ -14,7 +14,7 @@ interface PolicyRolesProps {
type SystemRole = (typeof SYSTEM_ROLES)[number]
const PolicyRoles = ({ selectedRoles, onUpdateSelectedRoles }: PolicyRolesProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { data, error, isLoading, isError, isSuccess } = useDatabaseRolesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
@@ -3,10 +3,10 @@ import { useEffect, useState } from 'react'
import { UseFormReturn } from 'react-hook-form'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseRolesQuery } from 'data/database-roles/database-roles-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
Button,
CommandEmpty_Shadcn_,
@@ -60,7 +60,7 @@ export const PolicyDetailsV2 = ({
onUpdateCommand,
authContext,
}: PolicyDetailsV2Props) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const [open, setOpen] = useState(false)
const canUpdatePolicies = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
@@ -16,7 +16,7 @@ import { useDatabasePolicyUpdateMutation } from 'data/database-policies/database
import { databasePoliciesKeys } from 'data/database-policies/keys'
import { QueryResponseError, useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
Button,
Checkbox_Shadcn_,
@@ -33,9 +33,9 @@ import {
cn,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { checkIfPolicyHasChanged, generateCreatePolicyQuery } from './PolicyEditorPanel.utils'
import { LockedCreateQuerySection, LockedRenameQuerySection } from './LockedQuerySection'
import { PolicyDetailsV2 } from './PolicyDetailsV2'
import { checkIfPolicyHasChanged, generateCreatePolicyQuery } from './PolicyEditorPanel.utils'
import { PolicyEditorPanelHeader } from './PolicyEditorPanelHeader'
import { PolicyTemplates } from './PolicyTemplates'
import { QueryError } from './QueryError'
@@ -65,7 +65,7 @@ export const PolicyEditorPanel = memo(function ({
}: PolicyEditorPanelProps) {
const { ref } = useParams()
const queryClient = useQueryClient()
const selectedProject = useSelectedProject()
const { data: selectedProject } = useSelectedProjectQuery()
const canUpdatePolicies = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
@@ -3,11 +3,11 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { noop } from 'lodash'
import { Edit, MoreVertical, Trash } from 'lucide-react'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
import Panel from 'components/ui/Panel'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import {
Badge,
@@ -40,7 +40,7 @@ const PolicyRow = ({
const aiSnap = useAiAssistantStateSnapshot()
const canUpdatePolicies = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'policies')
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { data: authConfig } = useAuthConfigQuery({ projectRef: project?.ref })
// override islocked for Realtime messages table
@@ -2,12 +2,12 @@ import type { PostgresPolicy } from '@supabase/postgres-meta'
import { noop } from 'lodash'
import { Info } from 'lucide-react'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import Panel from 'components/ui/Panel'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
import { useTableRolesAccessQuery } from 'data/tables/table-roles-access-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
import PolicyRow from './PolicyRow'
@@ -40,7 +40,7 @@ export const PolicyTableRow = ({
onSelectEditPolicy = noop,
onSelectDeletePolicy = noop,
}: PolicyTableRowProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
// [Joshen] Changes here are so that warnings are more accurate and granular instead of purely relying if RLS is disabled or enabled
// The following scenarios are technically okay if the table has RLS disabled, in which it won't be publicly readable / writable
@@ -12,7 +12,7 @@ import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
import {
AlertDescription_Shadcn_,
@@ -62,7 +62,7 @@ const UserSessionsSchema = z.object({
const SessionsAuthSettingsForm = () => {
const { ref: projectRef } = useParams()
const { data: authConfig, error: authConfigError, isError } = useAuthConfigQuery({ projectRef })
const { mutate: updateAuthConfig, isLoading: isUpdatingConfig } = useAuthConfigUpdateMutation()
const { mutate: updateAuthConfig } = useAuthConfigUpdateMutation()
// Separate loading states for each form
const [isUpdatingRefreshTokens, setIsUpdatingRefreshTokens] = useState(false)
@@ -71,7 +71,7 @@ const SessionsAuthSettingsForm = () => {
const canReadConfig = useCheckPermissions(PermissionAction.READ, 'custom_config_gotrue')
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
const organization = useSelectedOrganization()
const { data: organization } = useSelectedOrganizationQuery()
const isProPlanAndUp = organization?.plan?.id !== 'free'
const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
@@ -7,7 +7,6 @@ import { toast } from 'sonner'
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useIsAPIDocsSidePanelEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import APIDocsButton from 'components/ui/APIDocsButton'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
@@ -18,6 +17,7 @@ import { useUserDeleteMutation } from 'data/auth/user-delete-mutation'
import { useUsersCountQuery } from 'data/auth/users-count-query'
import { User, useUsersInfiniteQuery } from 'data/auth/users-infinite-query'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { isAtBottom } from 'lib/helpers'
import {
Button,
@@ -61,7 +61,7 @@ export type Filter = 'all' | 'verified' | 'unverified' | 'anonymous'
export const UsersV2 = () => {
const queryClient = useQueryClient()
const { ref: projectRef } = useParams()
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const gridRef = useRef<DataGridHandle>(null)
const xScroll = useRef<number>(0)
const isNewAPIDocsEnabled = useIsAPIDocsSidePanelEnabled()
@@ -7,7 +7,7 @@ import { toast } from 'sonner'
import { Modal } from 'ui'
import { useOrganizationPaymentMethodSetupIntent } from 'data/organizations/organization-payment-method-setup-intent-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { STRIPE_PUBLIC_KEY } from 'lib/constants'
import AddPaymentMethodForm from './AddPaymentMethodForm'
import { getStripeElementsAppearanceOptions } from './Payment.utils'
@@ -29,7 +29,7 @@ const AddNewPaymentMethodModal = ({
}: AddNewPaymentMethodModalProps) => {
const { resolvedTheme } = useTheme()
const [intent, setIntent] = useState<any>()
const selectedOrganization = useSelectedOrganization()
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
const [captchaRef, setCaptchaRef] = useState<HCaptcha | null>(null)
@@ -9,7 +9,7 @@ import { useOrganizationCustomerProfileUpdateMutation } from 'data/organizations
import { useOrganizationPaymentMethodMarkAsDefaultMutation } from 'data/organizations/organization-payment-method-default-mutation'
import { useOrganizationTaxIdQuery } from 'data/organizations/organization-tax-id-query'
import { useOrganizationTaxIdUpdateMutation } from 'data/organizations/organization-tax-id-update-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { isEqual } from 'lodash'
import { useRef, useState } from 'react'
import { toast } from 'sonner'
@@ -27,7 +27,7 @@ interface AddPaymentMethodFormProps {
// Small UX annoyance here, that the page will be refreshed
const AddPaymentMethodForm = ({ onCancel, onConfirm }: AddPaymentMethodFormProps) => {
const selectedOrganization = useSelectedOrganization()
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const { data: customerProfile, isLoading: customerProfileLoading } =
useOrganizationCustomerProfileQuery({
@@ -1,7 +1,7 @@
import { Forward, GitBranch } from 'lucide-react'
import { useBranchesQuery } from 'data/branches/branches-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PageType, useRegisterCommands, useRegisterPage, useSetPage } from 'ui-patterns/CommandMenu'
import { COMMAND_MENU_SECTIONS } from '../App/CommandMenu/CommandMenu.utils'
import { orderCommandSectionsByPriority } from '../App/CommandMenu/ordering'
@@ -12,7 +12,7 @@ const EMPTY_ARRAY = [] as Array<any>
export function useBranchCommands() {
const setPage = useSetPage()
const selectedProject = useSelectedProject()
const { data: selectedProject } = useSelectedProjectQuery()
const isBranchingEnabled = selectedProject?.is_branch_enabled === true
let { data: branches } = useBranchesQuery(
@@ -9,6 +9,7 @@ import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
import * as z from 'zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { useIsBranching2Enabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { BranchingPITRNotice } from 'components/layouts/AppLayout/EnableBranchingButton/BranchingPITRNotice'
@@ -23,8 +24,10 @@ import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-
import { projectKeys } from 'data/projects/keys'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useFlag } from 'hooks/ui/useFlag'
import { BASE_PATH, IS_PLATFORM } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
import {
@@ -46,22 +49,18 @@ import {
cn,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useFlag } from 'hooks/ui/useFlag'
export const CreateBranchModal = () => {
const allowDataBranching = useFlag('allowDataBranching')
const { ref } = useParams()
const router = useRouter()
const queryClient = useQueryClient()
const projectDetails = useSelectedProject()
const selectedOrg = useSelectedOrganization()
const { data: projectDetails } = useSelectedProjectQuery()
const { data: selectedOrg } = useSelectedOrganizationQuery()
const gitlessBranching = useIsBranching2Enabled()
const { showCreateBranchModal, setShowCreateBranchModal } = useAppStateSnapshot()
const organization = useSelectedOrganization()
const isProPlanAndUp = organization?.plan?.id !== 'free'
const isProPlanAndUp = selectedOrg?.plan?.id !== 'free'
const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp
const isBranch = projectDetails?.parent_project_ref !== undefined
@@ -8,15 +8,15 @@ import { toast } from 'sonner'
import * as z from 'zod'
import { useParams } from 'common'
import { useIsBranching2Enabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import AlertError from 'components/ui/AlertError'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
import { Branch, useBranchesQuery } from 'data/branches/branches-query'
import { useCheckGithubBranchValidity } from 'data/integrations/github-branch-check-query'
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useIsBranching2Enabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { BASE_PATH } from 'lib/constants'
import { useRouter } from 'next/router'
import {
@@ -48,8 +48,8 @@ interface EditBranchModalProps {
export const EditBranchModal = ({ branch, visible, onClose }: EditBranchModalProps) => {
const { ref } = useParams()
const router = useRouter()
const projectDetails = useSelectedProject()
const selectedOrg = useSelectedOrganization()
const { data: projectDetails } = useSelectedProjectQuery()
const { data: selectedOrg } = useSelectedOrganizationQuery()
const gitlessBranching = useIsBranching2Enabled()
const [isGitBranchValid, setIsGitBranchValid] = useState(false)
@@ -1,5 +1,8 @@
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { GitBranchIcon } from 'lucide-react'
import { useState } from 'react'
import { Button } from 'ui'
import {
AlertDialog,
AlertDialogAction,
@@ -10,12 +13,9 @@ import {
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Button,
} from 'ui'
import { GitBranchIcon } from 'lucide-react'
import { Admonition } from 'ui-patterns'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
interface OutOfDateNoticeProps {
isBranchOutOfDateMigrations: boolean
@@ -44,8 +44,8 @@ export const OutOfDateNotice = ({
}: OutOfDateNoticeProps) => {
const [isDialogOpen, setIsDialogOpen] = useState(false)
const hasOutdatedMigrations = isBranchOutOfDateMigrations && missingMigrationsCount > 0
const selectedOrg = useSelectedOrganization()
const project = useSelectedProject()
const { data: selectedOrg } = useSelectedOrganizationQuery()
const { data: project } = useSelectedProjectQuery()
const { mutate: sendEvent } = useSendEventMutation()
const isBranch = project?.parent_project_ref !== undefined
@@ -2,10 +2,13 @@ import dayjs from 'dayjs'
import { MoreVertical, X } from 'lucide-react'
import { useRouter } from 'next/router'
import { useQueryClient } from '@tanstack/react-query'
import { useParams } from 'common'
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
import type { Branch } from 'data/branches/branches-query'
import { branchKeys } from 'data/branches/keys'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { toast } from 'sonner'
import {
Button,
DropdownMenu,
@@ -13,9 +16,6 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from 'ui'
import { toast } from 'sonner'
import { useQueryClient } from '@tanstack/react-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
interface ReviewRowProps {
branch: Branch
@@ -23,7 +23,7 @@ interface ReviewRowProps {
export const ReviewRow = ({ branch }: ReviewRowProps) => {
const router = useRouter()
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const { ref: projectRef } = useParams()
const queryClient = useQueryClient()
@@ -1,12 +1,12 @@
import { AiIconAnimation } from 'ui'
import { useProjectByRef } from 'hooks/misc/useSelectedProject'
import { useTablesQuery } from 'data/tables/tables-query'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { Branch } from 'data/branches/branches-query'
import { tablesToSQL } from 'lib/helpers'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { Branch } from 'data/branches/branches-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useProjectByRefQuery } from 'hooks/misc/useSelectedProject'
import { tablesToSQL } from 'lib/helpers'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { AiIconAnimation } from 'ui'
interface ReviewWithAIProps {
currentBranch?: Branch
@@ -24,11 +24,11 @@ export const ReviewWithAI = ({
disabled = false,
}: ReviewWithAIProps) => {
const aiSnap = useAiAssistantStateSnapshot()
const selectedOrg = useSelectedOrganization()
const { data: selectedOrg } = useSelectedOrganizationQuery()
const { mutate: sendEvent } = useSendEventMutation()
// Get parent project for production schema
const parentProject = useProjectByRef(parentProjectRef)
const { data: parentProject } = useProjectByRefQuery(parentProjectRef)
// Fetch production schema tables
const { data: productionTables } = useTablesQuery(
@@ -10,7 +10,7 @@ import Panel from 'components/ui/Panel'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
import {
Button,
@@ -35,7 +35,7 @@ import ConnectTabContent from './ConnectTabContent'
export const Connect = () => {
const { ref: projectRef } = useParams()
const selectedProject = useSelectedProject()
const { data: selectedProject } = useSelectedProjectQuery()
const isActiveHealthy = selectedProject?.status === PROJECT_STATUS.ACTIVE_HEALTHY
const [showConnect, setShowConnect] = useQueryState(
@@ -7,7 +7,7 @@ import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query
import { usePgbouncerConfigQuery } from 'data/database/pgbouncer-config-query'
import { useSupavisorConfigurationQuery } from 'data/database/supavisor-configuration-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { pluckObjectFields } from 'lib/helpers'
import { cn } from 'ui'
import { getAddons } from '../Billing/Subscription/Subscription.utils'
@@ -30,7 +30,7 @@ interface ConnectContentTabProps extends HTMLAttributes<HTMLDivElement> {
const ConnectTabContent = forwardRef<HTMLDivElement, ConnectContentTabProps>(
({ projectKeys, filePath, ...props }, ref) => {
const { ref: projectRef } = useParams()
const selectedOrg = useSelectedOrganization()
const { data: selectedOrg } = useSelectedOrganizationQuery()
const allowPgBouncerSelection = useMemo(() => selectedOrg?.plan.id !== 'free', [selectedOrg])
const { data: settings } = useProjectSettingsV2Query({ projectRef })
@@ -12,7 +12,7 @@ import { useSupavisorConfigurationQuery } from 'data/database/supavisor-configur
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { IS_PLATFORM } from 'lib/constants'
import { pluckObjectFields } from 'lib/helpers'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
@@ -63,7 +63,7 @@ const StepLabel = ({
*/
export const DatabaseConnectionString = () => {
const { ref: projectRef } = useParams()
const org = useSelectedOrganization()
const { data: org } = useSelectedOrganizationQuery()
const state = useDatabaseSelectorStateSnapshot()
const [selectedTab, setSelectedTab] = useState<DatabaseConnectionType>('uri')
@@ -6,12 +6,12 @@ import { useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import Panel from 'components/ui/Panel'
import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useBackupRestoreMutation } from 'data/database/backup-restore-mutation'
import { DatabaseBackup, useBackupsQuery } from 'data/database/backups-query'
import { setProjectStatus } from 'data/projects/projects-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import BackupItem from './BackupItem'
@@ -23,7 +23,7 @@ const BackupsList = () => {
const queryClient = useQueryClient()
const { ref: projectRef } = useParams()
const { project: selectedProject } = useProjectContext()
const { data: selectedProject } = useSelectedProjectQuery()
const isHealthy = selectedProject?.status === PROJECT_STATUS.ACTIVE_HEALTHY
const [selectedBackup, setSelectedBackup] = useState<DatabaseBackup>()
@@ -1,7 +1,6 @@
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import Link from 'next/link'
import React from 'react'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Badge, NavMenu, NavMenuItem } from 'ui'
type Props = {
@@ -9,7 +8,7 @@ type Props = {
}
function DatabaseBackupsNav({ active }: Props) {
const { ref, cloud_provider } = useProjectContext()?.project || {}
const { ref, cloud_provider } = useSelectedProjectQuery()?.data || {}
const navMenuItems = [
{
@@ -1,7 +1,7 @@
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import Panel from 'components/ui/Panel'
import { useCloneBackupsQuery } from 'data/projects/clone-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Badge, Button } from 'ui'
import { TimestampInfo } from 'ui-patterns'
import BackupsEmpty from '../BackupsEmpty'
@@ -12,8 +12,8 @@ interface BackupsListProps {
}
export const BackupsList = ({ onSelectRestore, disabled }: BackupsListProps) => {
const { project } = useProjectContext()
const organization = useSelectedOrganization()
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
const isFreePlan = organization?.plan?.id === 'free'
@@ -1,5 +1,5 @@
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
Button,
Dialog,
@@ -26,8 +26,8 @@ export const ConfirmRestoreDialog = ({
onSelectContinue,
additionalMonthlySpend,
}: ConfirmRestoreDialogProps) => {
const { project } = useProjectContext()
const organization = useSelectedOrganization()
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
return (
<Dialog open={open} onOpenChange={onOpenChange}>
@@ -5,11 +5,11 @@ import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
import { z } from 'zod'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import PasswordStrengthBar from 'components/ui/PasswordStrengthBar'
import { useProjectCloneMutation } from 'data/projects/clone-mutation'
import { useCloneBackupsQuery } from 'data/projects/clone-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { passwordStrength } from 'lib/helpers'
import { generateStrongPassword } from 'lib/project'
import {
@@ -48,8 +48,8 @@ export const CreateNewProjectDialog = ({
onCloneSuccess,
additionalMonthlySpend,
}: CreateNewProjectDialogProps) => {
const { project } = useProjectContext()
const organization = useSelectedOrganization()
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
const [passwordStrengthScore, setPasswordStrengthScore] = useState(0)
const [passwordStrengthMessage, setPasswordStrengthMessage] = useState('')
@@ -1,9 +1,14 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { AlertCircle, ExternalLink, Plus } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useRef } from 'react'
import { DragDropContext, Droppable, DroppableProvided } from 'react-beautiful-dnd'
import { useFieldArray, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import * as z from 'zod'
import { useEnumeratedTypeCreateMutation } from 'data/enumerated-types/enumerated-type-create-mutation'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -20,13 +25,8 @@ import {
SidePanel,
cn,
} from 'ui'
import * as z from 'zod'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useEnumeratedTypeCreateMutation } from 'data/enumerated-types/enumerated-type-create-mutation'
import EnumeratedTypeValueRow from './EnumeratedTypeValueRow'
import { NATIVE_POSTGRES_TYPES } from './EnumeratedTypes.constants'
import { AlertCircle, ExternalLink, Plus } from 'lucide-react'
interface CreateEnumeratedTypeSidePanelProps {
visible: boolean
@@ -41,7 +41,7 @@ const CreateEnumeratedTypeSidePanel = ({
}: CreateEnumeratedTypeSidePanelProps) => {
const initialValues = { name: '', description: '', values: [{ value: '' }] }
const submitRef = useRef<HTMLButtonElement>(null)
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { mutate: createEnumeratedType, isLoading: isCreating } = useEnumeratedTypeCreateMutation({
onSuccess: (res, vars) => {
toast.success(`Successfully created type "${vars.name}"`)
@@ -1,6 +1,7 @@
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useEnumeratedTypeDeleteMutation } from 'data/enumerated-types/enumerated-type-delete-mutation'
import { toast } from 'sonner'
import { useEnumeratedTypeDeleteMutation } from 'data/enumerated-types/enumerated-type-delete-mutation'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
interface DeleteEnumeratedTypeModalProps {
@@ -14,7 +15,7 @@ const DeleteEnumeratedTypeModal = ({
selectedEnumeratedType,
onClose,
}: DeleteEnumeratedTypeModalProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { mutate: deleteEnumeratedType, isLoading: isDeleting } = useEnumeratedTypeDeleteMutation({
onSuccess: () => {
toast.success(`Successfully deleted "${selectedEnumeratedType.name}"`)
@@ -1,8 +1,15 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { AlertCircle, ExternalLink, Plus } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useRef } from 'react'
import { DragDropContext, Droppable, DroppableProvided } from 'react-beautiful-dnd'
import { useFieldArray, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import * as z from 'zod'
import { useEnumeratedTypeUpdateMutation } from 'data/enumerated-types/enumerated-type-update-mutation'
import type { EnumeratedType } from 'data/enumerated-types/enumerated-types-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -19,14 +26,7 @@ import {
SidePanel,
cn,
} from 'ui'
import * as z from 'zod'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useEnumeratedTypeUpdateMutation } from 'data/enumerated-types/enumerated-type-update-mutation'
import type { EnumeratedType } from 'data/enumerated-types/enumerated-types-query'
import { DragDropContext, Droppable, DroppableProvided } from 'react-beautiful-dnd'
import EnumeratedTypeValueRow from './EnumeratedTypeValueRow'
import { AlertCircle, ExternalLink, Plus } from 'lucide-react'
interface EditEnumeratedTypeSidePanelProps {
visible: boolean
@@ -40,7 +40,7 @@ const EditEnumeratedTypeSidePanel = ({
onClose,
}: EditEnumeratedTypeSidePanelProps) => {
const submitRef = useRef<HTMLButtonElement>(null)
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { mutate: updateEnumeratedType, isLoading: isCreating } = useEnumeratedTypeUpdateMutation({
onSuccess: (_, vars) => {
toast.success(`Successfully updated type "${vars.name.updated}"`)
@@ -1,18 +1,17 @@
import { Edit, MoreVertical, Search, Trash } from 'lucide-react'
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'
import {
EnumeratedType,
useEnumeratedTypesQuery,
} from 'data/enumerated-types/enumerated-types-query'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useIsProtectedSchema } from 'hooks/useProtectedSchemas'
import {
Button,
@@ -28,18 +27,13 @@ import DeleteEnumeratedTypeModal from './DeleteEnumeratedTypeModal'
import EditEnumeratedTypeSidePanel from './EditEnumeratedTypeSidePanel'
const EnumeratedTypes = () => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const [search, setSearch] = useState('')
const { selectedSchema, setSelectedSchema } = useQuerySchemaState()
const [showCreateTypePanel, setShowCreateTypePanel] = useState(false)
const [selectedTypeToEdit, setSelectedTypeToEdit] = useState<EnumeratedType>()
const [selectedTypeToDelete, setSelectedTypeToDelete] = useState<EnumeratedType>()
const { data: schemas } = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data, error, isLoading, isError, isSuccess } = useEnumeratedTypesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
@@ -3,13 +3,12 @@ import { Database, ExternalLinkIcon, Plus } from 'lucide-react'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { DocsButton } from 'components/ui/DocsButton'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useDatabaseExtensionEnableMutation } from 'data/database-extensions/database-extension-enable-mutation'
import { useSchemasQuery } from 'data/database/schemas-query'
import { executeSql } from 'data/sql/execute-sql-query'
import { useIsOrioleDb } from 'hooks/misc/useSelectedProject'
import { useIsOrioleDb, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -32,7 +31,7 @@ interface EnableExtensionModalProps {
}
const EnableExtensionModal = ({ visible, extension, onCancel }: EnableExtensionModalProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const isOrioleDb = useIsOrioleDb()
const [defaultSchema, setDefaultSchema] = useState()
const [fetchingSchemaInfo, setFetchingSchemaInfo] = useState(false)
@@ -4,12 +4,11 @@ import Link from 'next/link'
import { useState } from 'react'
import { toast } from 'sonner'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useDatabaseExtensionDisableMutation } from 'data/database-extensions/database-extension-disable-mutation'
import { DatabaseExtension } from 'data/database-extensions/database-extensions-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsOrioleDb } from 'hooks/misc/useSelectedProject'
import { useIsOrioleDb, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { extensions } from 'shared-data'
import { Button, cn, Switch, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { Admonition } from 'ui-patterns'
@@ -22,7 +21,7 @@ interface ExtensionCardProps {
}
const ExtensionCard = ({ extension }: ExtensionCardProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const isOn = extension.installed_version !== null
const isOrioleDb = useIsOrioleDb()
@@ -4,13 +4,13 @@ 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 { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { Input } from 'ui'
import ExtensionCard from './ExtensionCard'
import ExtensionCardSkeleton from './ExtensionCardSkeleton'
@@ -18,7 +18,7 @@ import { HIDDEN_EXTENSIONS, SEARCH_TERMS } from './Extensions.constants'
const Extensions = () => {
const { filter } = useParams()
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const [filterString, setFilterString] = useState<string>('')
const { data, isLoading } = useDatabaseExtensionsQuery({
@@ -7,12 +7,12 @@ import { toast } from 'sonner'
import z from 'zod'
import { POSTGRES_DATA_TYPES } from 'components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.constants'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import SchemaSelector from 'components/ui/SchemaSelector'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { useDatabaseFunctionCreateMutation } from 'data/database-functions/database-functions-create-mutation'
import { DatabaseFunction } from 'data/database-functions/database-functions-query'
import { useDatabaseFunctionUpdateMutation } from 'data/database-functions/database-functions-update-mutation'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useProtectedSchemas } from 'hooks/useProtectedSchemas'
import type { FormSchema } from 'types'
import {
@@ -69,7 +69,7 @@ const FormSchema = z.object({
})
const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const [isClosingPanel, setIsClosingPanel] = useState(false)
const [advancedSettingsShown, setAdvancedSettingsShown] = useState(false)
// For now, there's no AI assistant for functions
@@ -186,7 +186,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
layout="horizontal"
>
<FormControl_Shadcn_>
<Input_Shadcn_ {...field} />
<Input_Shadcn_ {...field} placeholder="Name of function" />
</FormControl_Shadcn_>
</FormItemLayout>
)}
@@ -601,7 +601,7 @@ const FormFieldConfigParams = ({ readonly }: FormFieldConfigParamsProps) => {
const ALL_ALLOWED_LANGUAGES = ['plpgsql', 'sql', 'plcoffee', 'plv8', 'plls']
const FormFieldLanguage = () => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { data: enabledExtensions } = useDatabaseExtensionsQuery(
{
@@ -1,8 +1,8 @@
import { toast } from 'sonner'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseFunctionDeleteMutation } from 'data/database-functions/database-functions-delete-mutation'
import { DatabaseFunction } from 'data/database-functions/database-functions-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
interface DeleteFunctionProps {
@@ -12,7 +12,7 @@ interface DeleteFunctionProps {
}
const DeleteFunction = ({ func, visible, setVisible }: DeleteFunctionProps) => {
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { name, schema } = func ?? {}
const { mutate: deleteDatabaseFunction, isLoading } = useDatabaseFunctionDeleteMutation({
@@ -3,11 +3,11 @@ import { includes, noop, sortBy } from 'lodash'
import { Edit, Edit2, FileText, MoreVertical, Trash } from 'lucide-react'
import { useRouter } from 'next/router'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import Table from 'components/to-be-cleaned/Table'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useDatabaseFunctionsQuery } from 'data/database-functions/database-functions-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import {
Button,
@@ -34,7 +34,7 @@ const FunctionList = ({
deleteFunction = noop,
}: FunctionListProps) => {
const router = useRouter()
const { project: selectedProject } = useProjectContext()
const { data: selectedProject } = useSelectedProjectQuery()
const aiSnap = useAiAssistantStateSnapshot()
const { data: functions } = useDatabaseFunctionsQuery({
@@ -112,7 +112,12 @@ const FunctionList = ({
{canUpdateFunctions ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button type="default" className="px-1" icon={<MoreVertical />} />
<Button
aria-label="More options"
type="default"
className="px-1"
icon={<MoreVertical />}
/>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" className="w-52">
{isApiDocumentAvailable && (
@@ -5,7 +5,6 @@ import { Search } from 'lucide-react'
import { useRouter } from 'next/router'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
@@ -16,6 +15,7 @@ import { useDatabaseFunctionsQuery } from 'data/database-functions/database-func
import { useSchemasQuery } from 'data/database/schemas-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useIsProtectedSchema } from 'hooks/useProtectedSchemas'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { AiIconAnimation, Input } from 'ui'
@@ -35,7 +35,7 @@ const FunctionsList = ({
}: FunctionsListProps) => {
const router = useRouter()
const { search } = useParams()
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const aiSnap = useAiAssistantStateSnapshot()
const { selectedSchema, setSelectedSchema } = useQuerySchemaState()
@@ -1,8 +1,8 @@
import type { PostgresTrigger } from '@supabase/postgres-meta'
import { toast } from 'sonner'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseTriggerDeleteMutation } from 'data/database-triggers/database-trigger-delete-mutation'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
interface DeleteHookModalProps {
@@ -14,7 +14,7 @@ interface DeleteHookModalProps {
const DeleteHookModal = ({ selectedHook, visible, onClose }: DeleteHookModalProps) => {
const { name, schema } = selectedHook ?? {}
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { mutate: deleteDatabaseTrigger, isLoading: isDeleting } = useDatabaseTriggerDeleteMutation(
{
onSuccess: () => {
@@ -4,11 +4,11 @@ import { useEffect, useMemo, useRef, useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseTriggerCreateMutation } from 'data/database-triggers/database-trigger-create-mutation'
import { useDatabaseTriggerUpdateMutation } from 'data/database-triggers/database-trigger-update-transaction-mutation'
import { getTableEditor } from 'data/table-editor/table-editor-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { isValidHttpUrl, uuidv4 } from 'lib/helpers'
import { Button, Form, SidePanel } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
@@ -85,7 +85,7 @@ export const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelP
}))
})
const { project } = useProjectContext()
const { data: project } = useSelectedProjectQuery()
const { data } = useTablesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
@@ -6,7 +6,7 @@ import { useParams } from 'common'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
import { useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { uuidv4 } from 'lib/helpers'
import { Checkbox, Input, Listbox, Radio, SidePanel } from 'ui'
import { HTTPArgument, isEdgeFunction } from './EditHookPanel'
@@ -45,7 +45,7 @@ export const FormContents = ({
submitRef,
}: FormContentsProps) => {
const { ref } = useParams()
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const restUrl = project?.restUrl
const restUrlTld = restUrl ? new URL(restUrl).hostname.split('.').pop() : 'co'
@@ -2,11 +2,11 @@ import { ChevronDown, Plus, X } from 'lucide-react'
import Link from 'next/link'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { uuidv4 } from 'lib/helpers'
import {
Button,
@@ -48,7 +48,7 @@ const HTTPRequestFields = ({
onRemoveParameter,
}: HTTPRequestFieldsProps) => {
const { ref } = useParams()
const { project: selectedProject } = useProjectContext()
const { data: selectedProject } = useSelectedProjectQuery()
const { data: functions } = useEdgeFunctionsQuery({ projectRef: ref })
const { data: apiKeys } = useAPIKeysQuery({ projectRef: ref, reveal: true })
Loaded 100 of 489 files, more files were not shown because too many files have changed in this diff. Show more