feat(studio): TanStack project routes — auth/logs/settings/functions (stack 5.2/6, from #46424) (#47118)

**Stack 5.2/6** of the TanStack Start migration (#46424) — second half
of the project routes (S5 was split for CodeRabbit's 150-file cap).
Stacked on **#47117** (5.1).

> [!NOTE]
> Same shape as 5.1 — thin route wrappers over the existing pages-router
components. With this PR every route is present, so `routeTree.gen.ts`
is now **byte-identical to the migration branch**.

## What's in this PR
- **Remaining project routes:** auth, logs, settings, observability,
functions, advisors, project-level integrations.
- **Supporting edits:** hoist `EdgeFunctionsIndexPageWrapper` out of
`getLayout`, `functions/secrets`, and move `DefaultLayout` to the root
for the logs page.
- `routeTree.gen.ts` regenerated for the full set.

## Verification
On top of S1–5.1: `studio` typecheck ✓, lint (0 errors) ✓, **Next build
✓ (181/181 pages)**.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Reorganized internal routing and page structure to improve navigation
and maintainability across project settings, logs, functions,
authentication, integrations, and observability sections.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-07-06 18:31:18 +08:00
1 parent 89bba95028
commit 4129c8954d
97 files changed
+3637 -76

No files matched your search

@@ -2,7 +2,7 @@ import { useFlag, useParams } from 'common'
import { ExternalLink, RefreshCw, Search, X } from 'lucide-react'
import { useRouter } from 'next/router'
import { parseAsString, parseAsStringLiteral, useQueryState } from 'nuqs'
import React, { useMemo, useRef } from 'react'
import React, { useMemo, useRef, type PropsWithChildren } from 'react'
import { Button, Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Input } from 'ui-patterns/DataInputs/Input'
@@ -231,41 +231,45 @@ const EdgeFunctionsPage: NextPageWithLayout = () => {
)
}
EdgeFunctionsPage.getLayout = (page: React.ReactElement) => {
return (
<DefaultLayout>
<EdgeFunctionsLayout title="Edge Functions">
<div className="w-full min-h-full flex flex-col items-stretch">
<PageHeader size="large">
<PageHeaderMeta>
<PageHeaderSummary>
<PageHeaderTitle>Edge Functions</PageHeaderTitle>
<PageHeaderDescription>
Run server-side logic close to your users
</PageHeaderDescription>
</PageHeaderSummary>
<PageHeaderAside>
<DocsButton href={`${DOCS_URL}/guides/functions`} />
<Button asChild variant="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions"
>
Examples
</a>
</Button>
{IS_PLATFORM && <DeployEdgeFunctionButton />}
</PageHeaderAside>
</PageHeaderMeta>
</PageHeader>
// Hoisted out of `getLayout` so the TanStack route can import it
// directly. Same body — accepts the page content as `children`.
export const EdgeFunctionsIndexPageWrapper = ({ children }: PropsWithChildren) => (
<>
<div className="w-full min-h-full flex flex-col items-stretch">
<PageHeader size="large">
<PageHeaderMeta>
<PageHeaderSummary>
<PageHeaderTitle>Edge Functions</PageHeaderTitle>
<PageHeaderDescription>Run server-side logic close to your users</PageHeaderDescription>
</PageHeaderSummary>
<PageHeaderAside>
<DocsButton href={`${DOCS_URL}/guides/functions`} />
<Button asChild variant="default" icon={<ExternalLink />}>
<a
target="_blank"
rel="noreferrer"
href="https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions"
>
Examples
</a>
</Button>
{IS_PLATFORM && <DeployEdgeFunctionButton />}
</PageHeaderAside>
</PageHeaderMeta>
</PageHeader>
{page}
</div>
</EdgeFunctionsLayout>
<TerminalInstructionsDialog />
</DefaultLayout>
)
}
{children}
</div>
<TerminalInstructionsDialog />
</>
)
EdgeFunctionsPage.getLayout = (page: React.ReactElement) => (
<DefaultLayout>
<EdgeFunctionsLayout title="Edge Functions">
<EdgeFunctionsIndexPageWrapper>{page}</EdgeFunctionsIndexPageWrapper>
</EdgeFunctionsLayout>
</DefaultLayout>
)
export default EdgeFunctionsPage
@@ -1,3 +1,4 @@
import type { PropsWithChildren } from 'react'
import { Admonition } from 'ui-patterns/admonition'
import { PageContainer } from 'ui-patterns/PageContainer'
import {
@@ -91,27 +92,29 @@ const SecretsPage: NextPageWithLayout = () => {
)
}
SecretsPage.getLayout = (page) => {
return (
<DefaultLayout>
<EdgeFunctionsLayout title="Secrets">
<div className="w-full min-h-full flex flex-col items-stretch">
<PageHeader size="large">
<PageHeaderMeta>
<PageHeaderSummary>
<PageHeaderTitle>Edge Function Secrets</PageHeaderTitle>
<PageHeaderDescription>
Manage encrypted values for your functions
</PageHeaderDescription>
</PageHeaderSummary>
</PageHeaderMeta>
</PageHeader>
// Hoisted out of `getLayout` so the TanStack route can import it
// directly. Same body — accepts the page content as `children`.
export const SecretsPageWrapper = ({ children }: PropsWithChildren) => (
<div className="w-full min-h-full flex flex-col items-stretch">
<PageHeader size="large">
<PageHeaderMeta>
<PageHeaderSummary>
<PageHeaderTitle>Edge Function Secrets</PageHeaderTitle>
<PageHeaderDescription>Manage encrypted values for your functions</PageHeaderDescription>
</PageHeaderSummary>
</PageHeaderMeta>
</PageHeader>
{page}
</div>
</EdgeFunctionsLayout>
</DefaultLayout>
)
}
{children}
</div>
)
SecretsPage.getLayout = (page) => (
<DefaultLayout>
<EdgeFunctionsLayout title="Secrets">
<SecretsPageWrapper>{page}</SecretsPageWrapper>
</EdgeFunctionsLayout>
</DefaultLayout>
)
export default SecretsPage
+13 -13
View File
@@ -26,29 +26,29 @@ export const LogPage: NextPageWithLayout = () => {
if (!logsEnabled) {
return (
<DefaultLayout>
<ProjectLayout browserTitle={{ section: 'Logs' }}>
<UnknownInterface urlBack={`/project/${ref}`} />
</ProjectLayout>
</DefaultLayout>
<ProjectLayout browserTitle={{ section: 'Logs' }}>
<UnknownInterface urlBack={`/project/${ref}`} />
</ProjectLayout>
)
}
if (isUnifiedLogsEnabled) {
return (
<DefaultLayout>
{/* Omit the generic product segment here; project/org context already makes the route clear. */}
<ProjectLayout browserTitle={{ section: 'Unified Logs' }}>
<UnifiedLogs />
</ProjectLayout>
</DefaultLayout>
// Omit the generic product segment here; project/org context already makes the route clear.
<ProjectLayout browserTitle={{ section: 'Unified Logs' }}>
<UnifiedLogs />
</ProjectLayout>
)
}
return null
}
// Don't use getLayout since we're handling layouts conditionally within the component
LogPage.getLayout = (page) => page
// DefaultLayout lives at the framework wrapper level, not in the page
// body — both runtimes provide it externally (Next via `getLayout`,
// TanStack via the parent `routes/project/$ref.tsx` shell). Wrapping it
// inline would double-mount its SidebarProvider/ProjectContextProvider
// chain under TanStack since the shell already supplies one.
LogPage.getLayout = (page) => <DefaultLayout>{page}</DefaultLayout>
export default LogPage
File diff suppressed because it is too large. Load diff
+9 -4
View File
@@ -1,7 +1,7 @@
import { createFileRoute } from '@tanstack/react-router'
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
import WizardLayout from '@/components/layouts/WizardLayout'
import Wizard from '@/pages/new/[slug]'
export const Route = createFileRoute('/new/$slug')({
@@ -9,13 +9,18 @@ export const Route = createFileRoute('/new/$slug')({
})
// Placed at top-level rather than under _app — the Next getLayout omits
// AppLayout, and uses PageLayout (not WizardLayout) inside DefaultLayout.
// AppLayout. The Next page wraps the wizard in a local `PageLayout` that is
// just `withAuth(WizardLayoutWithoutAuth)` (NOT the shared PageLayout), giving
// the centered, max-w-2xl wizard column. WizardLayout's default export is the
// same `withAuth(WizardLayout)`, so we use it here to preserve both the auth
// guard and that constrained width — the shared PageLayout rendered the form
// full-width.
function NewProjectRoute() {
return (
<DefaultLayout hideMobileMenu headerTitle="New project">
<PageLayout>
<WizardLayout>
<Wizard dehydratedState={undefined} />
</PageLayout>
</WizardLayout>
</DefaultLayout>
)
}
@@ -0,0 +1,37 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import AdvisorsLayout from '@/components/layouts/AdvisorsLayout/AdvisorsLayout'
export const Route = createFileRoute('/project/$ref/advisors')({
component: AdvisorsShell,
})
type AdvisorsStaticData = {
advisorsLayoutTitle?: string
// The `advisors/rules` sub-shell wraps in its own <AdvisorsLayout>
// (mirroring the existing AdvisorRulesLayout component). Without
// opting out here we'd double-wrap.
skipAdvisorsLayout?: boolean
}
function AdvisorsShell() {
// Scan the whole match chain — `skipAdvisorsLayout` is set on the
// sub-shell route, not on the leaf. Same pattern as functions.tsx.
const skip = useMatches({
select: (matches) =>
matches.some((m) => (m.staticData as AdvisorsStaticData | undefined)?.skipAdvisorsLayout),
})
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as AdvisorsStaticData | undefined)
?.advisorsLayoutTitle ?? '',
})
if (skip) return <Outlet />
return (
<AdvisorsLayout title={title}>
<Outlet />
</AdvisorsLayout>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import AdvisorsPerformancePage from '@/pages/project/[ref]/advisors/performance'
export const Route = createFileRoute('/project/$ref/advisors/performance')({
component: AdvisorsPerformanceRoute,
staticData: {
advisorsLayoutTitle: 'Linter',
},
})
function AdvisorsPerformanceRoute() {
return <AdvisorsPerformancePage dehydratedState={undefined} />
}
@@ -0,0 +1,57 @@
import { createFileRoute, Outlet } from '@tanstack/react-router'
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useIsAdvisorRulesEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import AdvisorsLayout from '@/components/layouts/AdvisorsLayout/AdvisorsLayout'
import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
import { FeaturePreviewBadge } from '@/components/ui/FeaturePreviewBadge'
export const Route = createFileRoute('/project/$ref/advisors/rules')({
component: AdvisorRulesShell,
// Opt out of the parent advisors.tsx shell's <AdvisorsLayout> wrap
// — we render our own with no title, plus a PageLayout tab strip,
// mirroring the existing AdvisorRulesLayout component.
staticData: {
skipAdvisorsLayout: true,
},
})
// Body inlined from `components/layouts/AdvisorsLayout/AdvisorRulesLayout.tsx`
// minus its outer <DefaultLayout> wrap (already provided by the
// `routes/project/$ref.tsx` shell). The Next-side AdvisorRulesLayout
// keeps working unchanged for the pages router; we duplicate the
// PageLayout config here so the TanStack route can re-use the same
// title + nav + feature-preview badge without coupling to the
// component's DefaultLayout-wrapping shape.
function AdvisorRulesShell() {
const { ref } = useParams()
const isAdvisorRulesEnabled = useIsAdvisorRulesEnabled()
return (
<AdvisorsLayout>
<PageLayout
title={
<span className="flex items-center gap-x-4">
Advisor Settings
{isAdvisorRulesEnabled && (
<FeaturePreviewBadge featureKey={LOCAL_STORAGE_KEYS.UI_PREVIEW_ADVISOR_RULES} />
)}
</span>
}
subtitle="Disable specific advisor categories or rules"
navigationItems={[
{
label: 'Security',
href: `/project/${ref}/advisors/rules/security`,
},
{
label: 'Performance',
href: `/project/${ref}/advisors/rules/performance`,
},
]}
>
<Outlet />
</PageLayout>
</AdvisorsLayout>
)
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import AdvisorPerformanceRulesPage from '@/pages/project/[ref]/advisors/rules/performance'
export const Route = createFileRoute('/project/$ref/advisors/rules/performance')({
component: AdvisorRulesPerformanceRoute,
})
function AdvisorRulesPerformanceRoute() {
return <AdvisorPerformanceRulesPage dehydratedState={undefined} />
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import AdvisorSecurityRulesPage from '@/pages/project/[ref]/advisors/rules/security'
export const Route = createFileRoute('/project/$ref/advisors/rules/security')({
component: AdvisorRulesSecurityRoute,
})
function AdvisorRulesSecurityRoute() {
return <AdvisorSecurityRulesPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import AdvisorsSecurityPage from '@/pages/project/[ref]/advisors/security'
export const Route = createFileRoute('/project/$ref/advisors/security')({
component: AdvisorsSecurityRoute,
staticData: {
advisorsLayoutTitle: 'Linter',
},
})
function AdvisorsSecurityRoute() {
return <AdvisorsSecurityPage dehydratedState={undefined} />
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import ApiDocsRedirect from '@/pages/project/[ref]/api/index'
export const Route = createFileRoute('/project/$ref/api/')({
component: ProjectApiIndexRoute,
})
function ProjectApiIndexRoute() {
return <ApiDocsRedirect dehydratedState={undefined} />
}
+40
View File
@@ -0,0 +1,40 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import AuthLayout from '@/components/layouts/AuthLayout/AuthLayout'
export const Route = createFileRoute('/project/$ref/auth')({
component: AuthShell,
})
function AuthShell() {
// Read the two staticData fields via `select` so the shell only re-renders
// when the actual selected values change. See routes/_app.tsx for why the
// bare `useMatches()` form is unsafe here.
const skipAuthLayout = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as { skipAuthLayout?: boolean } | undefined)
?.skipAuthLayout ?? false,
})
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as { authLayoutTitle?: string } | undefined)
?.authLayoutTitle ?? '',
})
// Some auth leaves wrap themselves in a sub-layout that internally already
// includes <AuthLayout> (AuthProvidersLayout, AuthEmailsLayout, and pages
// like third-party that inline that wrapping). Those routes set
// `staticData.skipAuthLayout: true` and render their own Outlet chain;
// otherwise we'd double-wrap with AuthLayout (and AuthLayout pulls in
// withAuth + ProjectLayout, so a double-wrap is observable beyond just
// visual nesting).
if (skipAuthLayout) {
return <Outlet />
}
return (
<AuthLayout title={title}>
<Outlet />
</AuthLayout>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import AuditLogsPage from '@/pages/project/[ref]/auth/audit-logs'
export const Route = createFileRoute('/project/$ref/auth/audit-logs')({
component: AuthAuditLogsRoute,
staticData: {
authLayoutTitle: 'Audit Logs',
},
})
function AuthAuditLogsRoute() {
return <AuditLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import Hooks from '@/pages/project/[ref]/auth/hooks'
export const Route = createFileRoute('/project/$ref/auth/hooks')({
component: AuthHooksRoute,
staticData: {
authLayoutTitle: 'Auth Hooks',
},
})
function AuthHooksRoute() {
return <Hooks dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import MfaPage from '@/pages/project/[ref]/auth/mfa'
export const Route = createFileRoute('/project/$ref/auth/mfa')({
component: AuthMfaRoute,
staticData: {
authLayoutTitle: 'Multi-Factor',
},
})
function AuthMfaRoute() {
return <MfaPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import OAuthApps from '@/pages/project/[ref]/auth/oauth-apps'
export const Route = createFileRoute('/project/$ref/auth/oauth-apps')({
component: AuthOAuthAppsRoute,
staticData: {
authLayoutTitle: 'OAuth Apps',
},
})
function AuthOAuthAppsRoute() {
return <OAuthApps dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import OAuthServerPage from '@/pages/project/[ref]/auth/oauth-server'
export const Route = createFileRoute('/project/$ref/auth/oauth-server')({
component: AuthOAuthServerRoute,
staticData: {
authLayoutTitle: 'OAuth Server',
},
})
function AuthOAuthServerRoute() {
return <OAuthServerPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import AuthOverview from '@/pages/project/[ref]/auth/overview'
export const Route = createFileRoute('/project/$ref/auth/overview')({
component: AuthOverviewRoute,
staticData: {
authLayoutTitle: 'Overview',
},
})
function AuthOverviewRoute() {
return <AuthOverview dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import PasskeysPage from '@/pages/project/[ref]/auth/passkeys'
export const Route = createFileRoute('/project/$ref/auth/passkeys')({
component: AuthPasskeysRoute,
staticData: {
authLayoutTitle: 'Passkeys',
},
})
function AuthPasskeysRoute() {
return <PasskeysPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import PerformancePage from '@/pages/project/[ref]/auth/performance'
export const Route = createFileRoute('/project/$ref/auth/performance')({
component: AuthPerformanceRoute,
staticData: {
authLayoutTitle: 'Performance',
},
})
function AuthPerformanceRoute() {
return <PerformancePage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import ProtectionPage from '@/pages/project/[ref]/auth/protection'
export const Route = createFileRoute('/project/$ref/auth/protection')({
component: AuthProtectionRoute,
staticData: {
authLayoutTitle: 'Attack Protection',
},
})
function AuthProtectionRoute() {
return <ProtectionPage dehydratedState={undefined} />
}
@@ -0,0 +1,22 @@
import { createFileRoute } from '@tanstack/react-router'
import { AuthProvidersLayout } from '@/components/layouts/AuthLayout/AuthProvidersLayout'
import ProvidersPage from '@/pages/project/[ref]/auth/providers'
export const Route = createFileRoute('/project/$ref/auth/providers')({
component: AuthProvidersRoute,
// AuthProvidersLayout wraps in <AuthLayout> internally; the auth.tsx
// shell skips its own AuthLayout wrap so we don't double-wrap (and
// double withAuth + ProjectLayout).
staticData: {
skipAuthLayout: true,
},
})
function AuthProvidersRoute() {
return (
<AuthProvidersLayout>
<ProvidersPage dehydratedState={undefined} />
</AuthProvidersLayout>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import RateLimitsPage from '@/pages/project/[ref]/auth/rate-limits'
export const Route = createFileRoute('/project/$ref/auth/rate-limits')({
component: AuthRateLimitsRoute,
staticData: {
authLayoutTitle: 'Rate Limits',
},
})
function AuthRateLimitsRoute() {
return <RateLimitsPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import SessionsPage from '@/pages/project/[ref]/auth/sessions'
export const Route = createFileRoute('/project/$ref/auth/sessions')({
component: AuthSessionsRoute,
staticData: {
authLayoutTitle: 'Sessions',
},
})
function AuthSessionsRoute() {
return <SessionsPage dehydratedState={undefined} />
}
@@ -0,0 +1,21 @@
import { createFileRoute } from '@tanstack/react-router'
import { AuthEmailsLayout } from '@/components/layouts/AuthLayout/AuthEmailsLayout'
import SmtpPage from '@/pages/project/[ref]/auth/smtp'
export const Route = createFileRoute('/project/$ref/auth/smtp')({
component: AuthSmtpRoute,
// AuthEmailsLayout wraps in <AuthLayout> internally; opt out of the
// auth.tsx shell wrap to avoid double-wrapping.
staticData: {
skipAuthLayout: true,
},
})
function AuthSmtpRoute() {
return (
<AuthEmailsLayout>
<SmtpPage dehydratedState={undefined} />
</AuthEmailsLayout>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import TemplatePage from '@/pages/project/[ref]/auth/templates/[templateId]'
export const Route = createFileRoute('/project/$ref/auth/templates/$templateId')({
component: AuthTemplateDetailRoute,
staticData: {
authLayoutTitle: 'Emails',
},
})
function AuthTemplateDetailRoute() {
return <TemplatePage dehydratedState={undefined} />
}
@@ -0,0 +1,23 @@
import { createFileRoute } from '@tanstack/react-router'
import { AuthEmailsLayout } from '@/components/layouts/AuthLayout/AuthEmailsLayout'
import TemplatesPage from '@/pages/project/[ref]/auth/templates/index'
export const Route = createFileRoute('/project/$ref/auth/templates/')({
component: AuthTemplatesIndexRoute,
// AuthEmailsLayout wraps in <AuthLayout> internally; opt out of the
// auth.tsx shell wrap to avoid double-wrapping. Sibling
// templates/$templateId.tsx does NOT use AuthEmailsLayout, so we
// can't share a templates.tsx sub-shell — each leaf wraps itself.
staticData: {
skipAuthLayout: true,
},
})
function AuthTemplatesIndexRoute() {
return (
<AuthEmailsLayout>
<TemplatesPage dehydratedState={undefined} />
</AuthEmailsLayout>
)
}
@@ -0,0 +1,18 @@
import { createFileRoute } from '@tanstack/react-router'
import ThirdPartyPage from '@/pages/project/[ref]/auth/third-party'
export const Route = createFileRoute('/project/$ref/auth/third-party')({
component: AuthThirdPartyRoute,
// The page body wraps itself in <AuthProvidersLayout>, which already
// includes <AuthLayout>. Tell the auth.tsx shell to skip its own
// AuthLayout wrap so we don't double-wrap (and double withAuth +
// ProjectLayout).
staticData: {
skipAuthLayout: true,
},
})
function AuthThirdPartyRoute() {
return <ThirdPartyPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import URLConfiguration from '@/pages/project/[ref]/auth/url-configuration'
export const Route = createFileRoute('/project/$ref/auth/url-configuration')({
component: AuthUrlConfigurationRoute,
staticData: {
authLayoutTitle: 'URL Configuration',
},
})
function AuthUrlConfigurationRoute() {
return <URLConfiguration dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import UsersPage from '@/pages/project/[ref]/auth/users'
export const Route = createFileRoute('/project/$ref/auth/users')({
component: AuthUsersRoute,
staticData: {
authLayoutTitle: 'Users',
},
})
function AuthUsersRoute() {
return <UsersPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import PoliciesPage from '@/pages/project/[ref]/database/policies'
export const Route = createFileRoute('/project/$ref/database/policies')({
component: DatabasePoliciesRoute,
staticData: {
databaseLayoutTitle: 'Policies',
},
})
function DatabasePoliciesRoute() {
return <PoliciesPage dehydratedState={undefined} />
}
@@ -0,0 +1,41 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import EdgeFunctionsLayout from '@/components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout'
export const Route = createFileRoute('/project/$ref/functions')({
component: FunctionsShell,
})
type FunctionsStaticData = {
functionsLayoutTitle?: string
// The $functionSlug routes wrap themselves in <EdgeFunctionDetailsLayout>,
// which already renders <EdgeFunctionsLayout> internally. Without
// opting out here we'd double-wrap.
skipFunctionsLayout?: boolean
}
function FunctionsShell() {
// `skipFunctionsLayout` is set on the $functionSlug sub-shell's
// staticData, not on the leaf — so checking only the leaf misses it
// and we end up double-wrapping (`functions.tsx` adds
// EdgeFunctionsLayout, then the sub-shell's EdgeFunctionDetailsLayout
// wraps EdgeFunctionsLayout again internally → duplicated sidebar).
// Scan the whole match chain instead.
const skip = useMatches({
select: (matches) =>
matches.some((m) => (m.staticData as FunctionsStaticData | undefined)?.skipFunctionsLayout),
})
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as FunctionsStaticData | undefined)
?.functionsLayoutTitle ?? '',
})
if (skip) return <Outlet />
return (
<EdgeFunctionsLayout title={title}>
<Outlet />
</EdgeFunctionsLayout>
)
}
@@ -0,0 +1,27 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import EdgeFunctionDetailsLayout from '@/components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout'
export const Route = createFileRoute('/project/$ref/functions/$functionSlug')({
component: FunctionDetailsShell,
// EdgeFunctionDetailsLayout already wraps <EdgeFunctionsLayout>
// internally. Tell the parent functions.tsx shell to skip its own
// EdgeFunctionsLayout wrap so we don't double-wrap.
staticData: {
skipFunctionsLayout: true,
},
})
function FunctionDetailsShell() {
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as { edgeFunctionDetailsTitle?: string } | undefined)
?.edgeFunctionDetailsTitle ?? '',
})
return (
<EdgeFunctionDetailsLayout title={title}>
<Outlet />
</EdgeFunctionDetailsLayout>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import CodePage from '@/pages/project/[ref]/functions/[functionSlug]/code'
export const Route = createFileRoute('/project/$ref/functions/$functionSlug/code')({
component: FunctionCodeRoute,
staticData: {
edgeFunctionDetailsTitle: 'Code',
},
})
function FunctionCodeRoute() {
return <CodePage />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import FunctionDetailsPage from '@/pages/project/[ref]/functions/[functionSlug]/details'
export const Route = createFileRoute('/project/$ref/functions/$functionSlug/details')({
component: FunctionDetailsRoute,
staticData: {
edgeFunctionDetailsTitle: 'Settings',
},
})
function FunctionDetailsRoute() {
return <FunctionDetailsPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import FunctionOverviewPage from '@/pages/project/[ref]/functions/[functionSlug]/index'
export const Route = createFileRoute('/project/$ref/functions/$functionSlug/')({
component: FunctionOverviewRoute,
staticData: {
edgeFunctionDetailsTitle: 'Overview',
},
})
function FunctionOverviewRoute() {
return <FunctionOverviewPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import FunctionInvocationsPage from '@/pages/project/[ref]/functions/[functionSlug]/invocations'
export const Route = createFileRoute('/project/$ref/functions/$functionSlug/invocations')({
component: FunctionInvocationsRoute,
staticData: {
edgeFunctionDetailsTitle: 'Invocations',
},
})
function FunctionInvocationsRoute() {
return <FunctionInvocationsPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import FunctionLogsPage from '@/pages/project/[ref]/functions/[functionSlug]/logs'
export const Route = createFileRoute('/project/$ref/functions/$functionSlug/logs')({
component: FunctionLogsRoute,
staticData: {
edgeFunctionDetailsTitle: 'Logs',
},
})
function FunctionLogsRoute() {
return <FunctionLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,20 @@
import { createFileRoute } from '@tanstack/react-router'
import EdgeFunctionsPage, {
EdgeFunctionsIndexPageWrapper,
} from '@/pages/project/[ref]/functions/index'
export const Route = createFileRoute('/project/$ref/functions/')({
component: FunctionsIndexRoute,
staticData: {
functionsLayoutTitle: 'Edge Functions',
},
})
function FunctionsIndexRoute() {
return (
<EdgeFunctionsIndexPageWrapper>
<EdgeFunctionsPage dehydratedState={undefined} />
</EdgeFunctionsIndexPageWrapper>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import NewFunctionPage from '@/pages/project/[ref]/functions/new'
export const Route = createFileRoute('/project/$ref/functions/new')({
component: FunctionsNewRoute,
staticData: {
functionsLayoutTitle: 'New',
},
})
function FunctionsNewRoute() {
return <NewFunctionPage />
}
@@ -0,0 +1,18 @@
import { createFileRoute } from '@tanstack/react-router'
import SecretsPage, { SecretsPageWrapper } from '@/pages/project/[ref]/functions/secrets'
export const Route = createFileRoute('/project/$ref/functions/secrets')({
component: FunctionsSecretsRoute,
staticData: {
functionsLayoutTitle: 'Secrets',
},
})
function FunctionsSecretsRoute() {
return (
<SecretsPageWrapper>
<SecretsPage dehydratedState={undefined} />
</SecretsPageWrapper>
)
}
+16
View File
@@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
import { ProjectLayoutWithAuth } from '@/components/layouts/ProjectLayout'
import HomePage from '@/pages/project/[ref]/index'
export const Route = createFileRoute('/project/$ref/')({
component: ProjectHomeRoute,
})
function ProjectHomeRoute() {
return (
<ProjectLayoutWithAuth>
<HomePage dehydratedState={undefined} />
</ProjectLayoutWithAuth>
)
}
@@ -0,0 +1,15 @@
import { createFileRoute, Outlet } from '@tanstack/react-router'
import { ProjectIntegrationsLayout } from '@/components/layouts/ProjectIntegrationsLayout'
export const Route = createFileRoute('/project/$ref/integrations')({
component: IntegrationsShell,
})
function IntegrationsShell() {
return (
<ProjectIntegrationsLayout>
<Outlet />
</ProjectIntegrationsLayout>
)
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import IntegrationPage from '@/pages/project/[ref]/integrations/[id]/[pageId]/[childId]/index'
export const Route = createFileRoute('/project/$ref/integrations/$id/$pageId/$childId/')({
component: IntegrationChildIdRoute,
})
function IntegrationChildIdRoute() {
return <IntegrationPage dehydratedState={undefined} />
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import IntegrationPage from '@/pages/project/[ref]/integrations/[id]/[pageId]/index'
export const Route = createFileRoute('/project/$ref/integrations/$id/$pageId/')({
component: IntegrationPageIdRoute,
})
function IntegrationPageIdRoute() {
return <IntegrationPage dehydratedState={undefined} />
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import IntegrationPage from '@/pages/project/[ref]/integrations/[id]/index'
export const Route = createFileRoute('/project/$ref/integrations/$id/')({
component: IntegrationIdRoute,
})
function IntegrationIdRoute() {
return <IntegrationPage dehydratedState={undefined} />
}
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router'
import IntegrationsPage from '@/pages/project/[ref]/integrations/index'
export const Route = createFileRoute('/project/$ref/integrations/')({
component: IntegrationsIndexRoute,
})
function IntegrationsIndexRoute() {
return <IntegrationsPage dehydratedState={undefined} />
}
+35
View File
@@ -0,0 +1,35 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import LogsLayout from '@/components/layouts/LogsLayout/LogsLayout'
export const Route = createFileRoute('/project/$ref/logs')({
component: LogsShell,
})
type LogsStaticData = {
logsLayoutTitle?: string
// `logs/index` handles its own ProjectLayout-wrapped content
// (UnifiedLogs view or no-permission interface) and only needs the
// parent project shell's DefaultLayout — not LogsLayout. Opt out.
skipLogsLayout?: boolean
}
function LogsShell() {
const skip = useMatches({
select: (matches) =>
matches.some((m) => (m.staticData as LogsStaticData | undefined)?.skipLogsLayout),
})
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as LogsStaticData | undefined)?.logsLayoutTitle ??
'',
})
if (skip) return <Outlet />
return (
<LogsLayout title={title}>
<Outlet />
</LogsLayout>
)
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import AuthLogsPage from '@/pages/project/[ref]/logs/auth-logs'
export const Route = createFileRoute('/project/$ref/logs/auth-logs')({
component: AuthLogsRoute,
staticData: { logsLayoutTitle: 'Auth Logs' },
})
function AuthLogsRoute() {
return <AuthLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import CronLogsPage from '@/pages/project/[ref]/logs/cron-logs'
export const Route = createFileRoute('/project/$ref/logs/cron-logs')({
component: CronLogsRoute,
staticData: { logsLayoutTitle: 'Cron Logs' },
})
function CronLogsRoute() {
return <CronLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import DedicatedPoolerLogsPage from '@/pages/project/[ref]/logs/dedicated-pooler-logs'
export const Route = createFileRoute('/project/$ref/logs/dedicated-pooler-logs')({
component: DedicatedPoolerLogsRoute,
staticData: { logsLayoutTitle: 'Dedicated Pooler Logs' },
})
function DedicatedPoolerLogsRoute() {
return <DedicatedPoolerLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import EdgeFunctionsLogsPage from '@/pages/project/[ref]/logs/edge-functions-logs'
export const Route = createFileRoute('/project/$ref/logs/edge-functions-logs')({
component: EdgeFunctionsLogsRoute,
staticData: { logsLayoutTitle: 'Edge Functions Logs' },
})
function EdgeFunctionsLogsRoute() {
return <EdgeFunctionsLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import EdgeLogsPage from '@/pages/project/[ref]/logs/edge-logs'
export const Route = createFileRoute('/project/$ref/logs/edge-logs')({
component: EdgeLogsRoute,
staticData: { logsLayoutTitle: 'Edge Logs' },
})
function EdgeLogsRoute() {
return <EdgeLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import LogsExplorerPage from '@/pages/project/[ref]/logs/explorer/index'
export const Route = createFileRoute('/project/$ref/logs/explorer/')({
component: LogsExplorerIndexRoute,
staticData: { logsLayoutTitle: 'Explorer' },
})
function LogsExplorerIndexRoute() {
return <LogsExplorerPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import LogsRecentPage from '@/pages/project/[ref]/logs/explorer/recent'
export const Route = createFileRoute('/project/$ref/logs/explorer/recent')({
component: LogsExplorerRecentRoute,
staticData: { logsLayoutTitle: 'Recent' },
})
function LogsExplorerRecentRoute() {
return <LogsRecentPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import LogsSavedPage from '@/pages/project/[ref]/logs/explorer/saved'
export const Route = createFileRoute('/project/$ref/logs/explorer/saved')({
component: LogsExplorerSavedRoute,
staticData: { logsLayoutTitle: 'Saved' },
})
function LogsExplorerSavedRoute() {
return <LogsSavedPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import LogsTemplatesPage from '@/pages/project/[ref]/logs/explorer/templates'
export const Route = createFileRoute('/project/$ref/logs/explorer/templates')({
component: LogsExplorerTemplatesRoute,
staticData: { logsLayoutTitle: 'Templates' },
})
function LogsExplorerTemplatesRoute() {
return <LogsTemplatesPage dehydratedState={undefined} />
}
@@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
import LogPage from '@/pages/project/[ref]/logs/index'
export const Route = createFileRoute('/project/$ref/logs/')({
component: LogsIndexRoute,
staticData: {
// Page body wraps in its own <ProjectLayout> conditionally; LogsLayout
// is not appropriate here.
skipLogsLayout: true,
},
})
function LogsIndexRoute() {
return <LogPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import MultigresLogsPage from '@/pages/project/[ref]/logs/multigres-logs'
export const Route = createFileRoute('/project/$ref/logs/multigres-logs')({
component: MultigresLogsRoute,
staticData: { logsLayoutTitle: 'Multigres Logs' },
})
function MultigresLogsRoute() {
return <MultigresLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import PgUpgradeLogsPage from '@/pages/project/[ref]/logs/pg-upgrade-logs'
export const Route = createFileRoute('/project/$ref/logs/pg-upgrade-logs')({
component: PgUpgradeLogsRoute,
staticData: { logsLayoutTitle: 'Postgres Version Upgrade' },
})
function PgUpgradeLogsRoute() {
return <PgUpgradeLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import PgCronLogsPage from '@/pages/project/[ref]/logs/pgcron-logs'
export const Route = createFileRoute('/project/$ref/logs/pgcron-logs')({
component: PgCronLogsRoute,
staticData: { logsLayoutTitle: 'PgCron Logs' },
})
function PgCronLogsRoute() {
return <PgCronLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import PoolerLogsPage from '@/pages/project/[ref]/logs/pooler-logs'
export const Route = createFileRoute('/project/$ref/logs/pooler-logs')({
component: PoolerLogsRoute,
staticData: { logsLayoutTitle: 'Pooler Logs' },
})
function PoolerLogsRoute() {
return <PoolerLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import PostgresLogsPage from '@/pages/project/[ref]/logs/postgres-logs'
export const Route = createFileRoute('/project/$ref/logs/postgres-logs')({
component: PostgresLogsRoute,
staticData: { logsLayoutTitle: 'Postgres Logs' },
})
function PostgresLogsRoute() {
return <PostgresLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import PostgrestLogsPage from '@/pages/project/[ref]/logs/postgrest-logs'
export const Route = createFileRoute('/project/$ref/logs/postgrest-logs')({
component: PostgrestLogsRoute,
staticData: { logsLayoutTitle: 'Postgrest Logs' },
})
function PostgrestLogsRoute() {
return <PostgrestLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import RealtimeLogsPage from '@/pages/project/[ref]/logs/realtime-logs'
export const Route = createFileRoute('/project/$ref/logs/realtime-logs')({
component: RealtimeLogsRoute,
staticData: { logsLayoutTitle: 'Realtime Logs' },
})
function RealtimeLogsRoute() {
return <RealtimeLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ReplicationLogsPage from '@/pages/project/[ref]/logs/replication-logs'
export const Route = createFileRoute('/project/$ref/logs/replication-logs')({
component: ReplicationLogsRoute,
staticData: { logsLayoutTitle: 'Replication Logs' },
})
function ReplicationLogsRoute() {
return <ReplicationLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import StorageLogsPage from '@/pages/project/[ref]/logs/storage-logs'
export const Route = createFileRoute('/project/$ref/logs/storage-logs')({
component: StorageLogsRoute,
staticData: { logsLayoutTitle: 'Storage Logs' },
})
function StorageLogsRoute() {
return <StorageLogsPage dehydratedState={undefined} />
}
@@ -0,0 +1,21 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
export const Route = createFileRoute('/project/$ref/observability')({
component: ObservabilityShell,
})
function ObservabilityShell() {
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as { observabilityLayoutTitle?: string } | undefined)
?.observabilityLayoutTitle ?? '',
})
return (
<ObservabilityLayout title={title}>
<Outlet />
</ObservabilityLayout>
)
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import ObservabilityReportPage from '@/pages/project/[ref]/observability/[id]'
export const Route = createFileRoute('/project/$ref/observability/$id')({
component: ObservabilityReportRoute,
staticData: {
observabilityLayoutTitle: 'Report',
},
})
function ObservabilityReportRoute() {
return <ObservabilityReportPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import ApiReport from '@/pages/project/[ref]/observability/api-overview'
export const Route = createFileRoute('/project/$ref/observability/api-overview')({
component: ObservabilityApiOverviewRoute,
staticData: {
observabilityLayoutTitle: 'API Gateway',
},
})
function ObservabilityApiOverviewRoute() {
return <ApiReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import AuthReport from '@/pages/project/[ref]/observability/auth'
export const Route = createFileRoute('/project/$ref/observability/auth')({
component: ObservabilityAuthRoute,
staticData: {
observabilityLayoutTitle: 'Auth',
},
})
function ObservabilityAuthRoute() {
return <AuthReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import DatabaseReport from '@/pages/project/[ref]/observability/database'
export const Route = createFileRoute('/project/$ref/observability/database')({
component: ObservabilityDatabaseRoute,
staticData: {
observabilityLayoutTitle: 'Database',
},
})
function ObservabilityDatabaseRoute() {
return <DatabaseReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import EdgeFunctionsReportV2 from '@/pages/project/[ref]/observability/edge-functions'
export const Route = createFileRoute('/project/$ref/observability/edge-functions')({
component: ObservabilityEdgeFunctionsRoute,
staticData: {
observabilityLayoutTitle: 'Edge Functions',
},
})
function ObservabilityEdgeFunctionsRoute() {
return <EdgeFunctionsReportV2 dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import UserReportPage from '@/pages/project/[ref]/observability/index'
export const Route = createFileRoute('/project/$ref/observability/')({
component: ObservabilityIndexRoute,
staticData: {
observabilityLayoutTitle: 'Overview',
},
})
function ObservabilityIndexRoute() {
return <UserReportPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import PostgRESTReport from '@/pages/project/[ref]/observability/postgrest'
export const Route = createFileRoute('/project/$ref/observability/postgrest')({
component: ObservabilityPostgRESTRoute,
staticData: {
observabilityLayoutTitle: 'PostgREST',
},
})
function ObservabilityPostgRESTRoute() {
return <PostgRESTReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import QueryInsightsReport from '@/pages/project/[ref]/observability/query-insights'
export const Route = createFileRoute('/project/$ref/observability/query-insights')({
component: ObservabilityQueryInsightsRoute,
staticData: {
observabilityLayoutTitle: 'Query insights',
},
})
function ObservabilityQueryInsightsRoute() {
return <QueryInsightsReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import QueryPerformanceReport from '@/pages/project/[ref]/observability/query-performance'
export const Route = createFileRoute('/project/$ref/observability/query-performance')({
component: ObservabilityQueryPerformanceRoute,
staticData: {
observabilityLayoutTitle: 'Query Performance',
},
})
function ObservabilityQueryPerformanceRoute() {
return <QueryPerformanceReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import RealtimeReport from '@/pages/project/[ref]/observability/realtime'
export const Route = createFileRoute('/project/$ref/observability/realtime')({
component: ObservabilityRealtimeRoute,
staticData: {
observabilityLayoutTitle: 'Realtime',
},
})
function ObservabilityRealtimeRoute() {
return <RealtimeReport dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import StorageReport from '@/pages/project/[ref]/observability/storage'
export const Route = createFileRoute('/project/$ref/observability/storage')({
component: ObservabilityStorageRoute,
staticData: {
observabilityLayoutTitle: 'Storage',
},
})
function ObservabilityStorageRoute() {
return <StorageReport dehydratedState={undefined} />
}
@@ -0,0 +1,34 @@
import { createFileRoute, Outlet, useMatches } from '@tanstack/react-router'
import SettingsLayout from '@/components/layouts/ProjectSettingsLayout/SettingsLayout'
export const Route = createFileRoute('/project/$ref/settings')({
component: SettingsShell,
})
type SettingsStaticData = {
settingsLayoutTitle?: string
// settings/api is a redirect-only page; skip the SettingsLayout wrap
// so we don't render the sidebar around a `return null`.
skipSettingsLayout?: boolean
}
function SettingsShell() {
const skip = useMatches({
select: (matches) =>
matches.some((m) => (m.staticData as SettingsStaticData | undefined)?.skipSettingsLayout),
})
const title = useMatches({
select: (matches) =>
(matches[matches.length - 1]?.staticData as SettingsStaticData | undefined)
?.settingsLayoutTitle ?? '',
})
if (skip) return <Outlet />
return (
<SettingsLayout title={title}>
<Outlet />
</SettingsLayout>
)
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectAddons from '@/pages/project/[ref]/settings/addons'
export const Route = createFileRoute('/project/$ref/settings/addons')({
component: SettingsAddonsRoute,
staticData: { settingsLayoutTitle: 'Add-ons' },
})
function SettingsAddonsRoute() {
return <ProjectAddons dehydratedState={undefined} />
}
@@ -0,0 +1,21 @@
import { createFileRoute, Outlet } from '@tanstack/react-router'
import ApiKeysLayout from '@/components/layouts/APIKeys/APIKeysLayout'
export const Route = createFileRoute('/project/$ref/settings/api-keys')({
component: ApiKeysShell,
})
// Sub-shell mirroring the existing pages-router pattern: both
// api-keys/index and api-keys/legacy wrap their content in
// <SettingsLayout title=...><ApiKeysLayout>{page}</ApiKeysLayout></SettingsLayout>.
// The parent settings.tsx shell handles SettingsLayout (title via the
// leaf's `settingsLayoutTitle` staticData); this sub-shell adds the
// ApiKeysLayout tab strip both leaves share.
function ApiKeysShell() {
return (
<ApiKeysLayout>
<Outlet />
</ApiKeysLayout>
)
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ApiKeysNewPage from '@/pages/project/[ref]/settings/api-keys/index'
export const Route = createFileRoute('/project/$ref/settings/api-keys/')({
component: SettingsApiKeysIndexRoute,
staticData: { settingsLayoutTitle: 'API Keys' },
})
function SettingsApiKeysIndexRoute() {
return <ApiKeysNewPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ApiKeysLegacyPage from '@/pages/project/[ref]/settings/api-keys/legacy'
export const Route = createFileRoute('/project/$ref/settings/api-keys/legacy')({
component: SettingsApiKeysLegacyRoute,
staticData: { settingsLayoutTitle: 'API Keys (Legacy)' },
})
function SettingsApiKeysLegacyRoute() {
return <ApiKeysLegacyPage dehydratedState={undefined} />
}
@@ -0,0 +1,14 @@
import { createFileRoute } from '@tanstack/react-router'
import ApiSettings from '@/pages/project/[ref]/settings/api'
export const Route = createFileRoute('/project/$ref/settings/api')({
component: SettingsApiRoute,
// Page is a useEffect redirect that returns null; SettingsLayout
// (the sidebar shell) shouldn't render around it.
staticData: { skipSettingsLayout: true },
})
function SettingsApiRoute() {
return <ApiSettings dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectBillingUsage from '@/pages/project/[ref]/settings/billing/usage'
export const Route = createFileRoute('/project/$ref/settings/billing/usage')({
component: SettingsBillingUsageRoute,
staticData: { settingsLayoutTitle: 'Usage' },
})
function SettingsBillingUsageRoute() {
return <ProjectBillingUsage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ComputeAndDiskPage from '@/pages/project/[ref]/settings/compute-and-disk'
export const Route = createFileRoute('/project/$ref/settings/compute-and-disk')({
component: SettingsComputeAndDiskRoute,
staticData: { settingsLayoutTitle: 'Compute and Disk' },
})
function SettingsComputeAndDiskRoute() {
return <ComputeAndDiskPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import Preferences from '@/pages/project/[ref]/settings/dashboard'
export const Route = createFileRoute('/project/$ref/settings/dashboard')({
component: SettingsDashboardRoute,
staticData: { settingsLayoutTitle: 'General' },
})
function SettingsDashboardRoute() {
return <Preferences dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectSettings from '@/pages/project/[ref]/settings/general'
export const Route = createFileRoute('/project/$ref/settings/general')({
component: SettingsGeneralRoute,
staticData: { settingsLayoutTitle: 'General' },
})
function SettingsGeneralRoute() {
return <ProjectSettings dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectInfrastructure from '@/pages/project/[ref]/settings/infrastructure'
export const Route = createFileRoute('/project/$ref/settings/infrastructure')({
component: SettingsInfrastructureRoute,
staticData: { settingsLayoutTitle: 'Infrastructure' },
})
function SettingsInfrastructureRoute() {
return <ProjectInfrastructure dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectSettingsIntegrations from '@/pages/project/[ref]/settings/integrations'
export const Route = createFileRoute('/project/$ref/settings/integrations')({
component: SettingsIntegrationsRoute,
staticData: { settingsLayoutTitle: 'Integrations' },
})
function SettingsIntegrationsRoute() {
return <ProjectSettingsIntegrations dehydratedState={undefined} />
}
@@ -0,0 +1,20 @@
import { createFileRoute } from '@tanstack/react-router'
import JWTKeysLayout from '@/components/layouts/JWTKeys/JWTKeysLayout'
import JWTSigningKeysPage from '@/pages/project/[ref]/settings/jwt/index'
export const Route = createFileRoute('/project/$ref/settings/jwt/')({
component: SettingsJwtIndexRoute,
staticData: { settingsLayoutTitle: 'JWT Keys' },
})
// Inline the JWTKeysLayout wrap here rather than via a `settings/jwt.tsx`
// sub-shell — jwt/legacy doesn't use JWTKeysLayout, so a shared
// sub-shell would wrap legacy in the wrong layout.
function SettingsJwtIndexRoute() {
return (
<JWTKeysLayout>
<JWTSigningKeysPage dehydratedState={undefined} />
</JWTKeysLayout>
)
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import JWTKeysLegacyPage from '@/pages/project/[ref]/settings/jwt/legacy'
export const Route = createFileRoute('/project/$ref/settings/jwt/legacy')({
component: SettingsJwtLegacyRoute,
staticData: { settingsLayoutTitle: 'JWT Keys (Legacy)' },
})
function SettingsJwtLegacyRoute() {
return <JWTKeysLegacyPage dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import LogDrainsSettings from '@/pages/project/[ref]/settings/log-drains'
export const Route = createFileRoute('/project/$ref/settings/log-drains')({
component: SettingsLogDrainsRoute,
staticData: { settingsLayoutTitle: 'Log Drains' },
})
function SettingsLogDrainsRoute() {
return <LogDrainsSettings dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectWebhookEndpointSettings from '@/pages/project/[ref]/settings/webhooks/[endpointId]'
export const Route = createFileRoute('/project/$ref/settings/webhooks/$endpointId')({
component: SettingsWebhooksEndpointRoute,
staticData: { settingsLayoutTitle: 'Webhooks' },
})
function SettingsWebhooksEndpointRoute() {
return <ProjectWebhookEndpointSettings dehydratedState={undefined} />
}
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import ProjectWebhooksSettings from '@/pages/project/[ref]/settings/webhooks/index'
export const Route = createFileRoute('/project/$ref/settings/webhooks/')({
component: SettingsWebhooksIndexRoute,
staticData: { settingsLayoutTitle: 'Webhooks' },
})
function SettingsWebhooksIndexRoute() {
return <ProjectWebhooksSettings dehydratedState={undefined} />
}