diff --git a/.gitignore b/.gitignore
index a2566899919aa..6f13c135c9bb4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -156,4 +156,7 @@ gcloud.json
keys.json
# Playwright MCP
-.playwright-mcp/*
\ No newline at end of file
+.playwright-mcp/*
+
+# Application
+**/.superset/**
diff --git a/apps/docs/components/GuidesSidebar.tsx b/apps/docs/components/GuidesSidebar.tsx
index ee4e8c49b4be2..0fc561ff94f43 100644
--- a/apps/docs/components/GuidesSidebar.tsx
+++ b/apps/docs/components/GuidesSidebar.tsx
@@ -19,16 +19,10 @@ interface TOCHeader {
function AiTools({ className }: { className?: string }) {
const [copied, setCopied] = useState(false)
- let url = ''
-
- // Safe check for server side rendering.
- try {
- const urlParts = new URL(`${window.location}`)
- url = urlParts.origin + urlParts.pathname
- } catch (error) {}
+ const path = usePathname()
async function copyMarkdown() {
- const mdUrl = `${url}.md`
+ const mdUrl = `/docs/${path}.md`
try {
const res = await fetch(mdUrl)
@@ -62,7 +56,7 @@ function AiTools({ className }: { className?: string }) {
{copied ? 'Copied!' : 'Copy as Markdown'}
void
disabled?: boolean
+ hasAccess?: boolean
}
-export const BackupsList = ({ onSelectRestore, disabled }: BackupsListProps) => {
+export const BackupsList = ({ onSelectRestore, disabled, hasAccess }: BackupsListProps) => {
const { data: project } = useSelectedProjectQuery()
- const { data: organization } = useSelectedOrganizationQuery()
-
- const isFreePlan = organization?.plan?.id === 'free'
-
const { data: cloneBackups } = useCloneBackupsQuery(
{ projectRef: project?.ref },
- { enabled: !isFreePlan }
+ { enabled: hasAccess }
)
return (
diff --git a/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx b/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx
index a57a29ef73829..b93a387c38293 100644
--- a/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx
+++ b/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx
@@ -7,7 +7,6 @@ import { z } from 'zod'
import { PasswordStrengthBar } from 'components/ui/PasswordStrengthBar'
import { useProjectCloneMutation } from 'data/projects/clone-mutation'
import { useCloneBackupsQuery } from 'data/projects/clone-query'
-import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { passwordStrength, PasswordStrengthScore } from 'lib/password-strength'
import { generateStrongPassword } from 'lib/project'
@@ -37,6 +36,7 @@ interface CreateNewProjectDialogProps {
onOpenChange: (value: boolean) => void
onCloneSuccess: () => void
additionalMonthlySpend: NewProjectPrice
+ hasAccess?: boolean
}
export const CreateNewProjectDialog = ({
@@ -46,10 +46,9 @@ export const CreateNewProjectDialog = ({
onOpenChange,
onCloneSuccess,
additionalMonthlySpend,
+ hasAccess,
}: CreateNewProjectDialogProps) => {
const { data: project } = useSelectedProjectQuery()
- const { data: organization } = useSelectedOrganizationQuery()
-
const [passwordStrengthScore, setPasswordStrengthScore] = useState(0)
const [passwordStrengthMessage, setPasswordStrengthMessage] = useState('')
@@ -66,11 +65,9 @@ export const CreateNewProjectDialog = ({
},
})
- const isFreePlan = organization?.plan?.id === 'free'
-
const { data: cloneBackups } = useCloneBackupsQuery(
{ projectRef: project?.ref },
- { enabled: !isFreePlan }
+ { enabled: hasAccess }
)
const hasPITREnabled = cloneBackups?.pitr_enabled
diff --git a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx
index 8cf054582daf7..b73c6628a5961 100644
--- a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx
+++ b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx
@@ -18,6 +18,7 @@ import { UpgradeToPro } from 'components/ui/UpgradeToPro'
import { useDiskAttributesQuery } from 'data/config/disk-attributes-query'
import { useCloneBackupsQuery } from 'data/projects/clone-query'
import { useCloneStatusQuery } from 'data/projects/clone-status-query'
+import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import {
@@ -35,7 +36,8 @@ import { PreviousRestoreItem } from './PreviousRestoreItem'
export const RestoreToNewProject = () => {
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
- const isFreePlan = organization?.plan?.id === 'free'
+ const { hasAccess: hasAccessToRestoreToNewProject, isLoading: isLoadingEntitlement } =
+ useCheckEntitlements('backup.restore_to_new_project')
const isOrioleDb = useIsOrioleDb()
const isAwsK8s = useIsAwsK8sCloudProvider()
@@ -50,7 +52,10 @@ export const RestoreToNewProject = () => {
error,
isPending: cloneBackupsLoading,
isError,
- } = useCloneBackupsQuery({ projectRef: project?.ref }, { enabled: !isFreePlan })
+ } = useCloneBackupsQuery(
+ { projectRef: project?.ref },
+ { enabled: hasAccessToRestoreToNewProject }
+ )
const isActiveHealthy = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
@@ -102,7 +107,11 @@ export const RestoreToNewProject = () => {
const isRestoring = previousClones?.some((c) => c.status === 'IN_PROGRESS')
const restoringClone = previousClones?.find((c) => c.status === 'IN_PROGRESS')
- if (isFreePlan) {
+ if (isLoadingEntitlement) {
+ return
+ }
+
+ if (!hasAccessToRestoreToNewProject) {
return (
{
selectedBackupId={selectedBackupId}
recoveryTimeTarget={recoveryTimeTarget}
additionalMonthlySpend={additionalMonthlySpend}
+ hasAccess={hasAccessToRestoreToNewProject}
onOpenChange={setShowNewProjectDialog}
onCloneSuccess={() => {
refetchCloneStatus()
@@ -307,6 +317,7 @@ export const RestoreToNewProject = () => {
) : (
{
setSelectedBackupId(id)
setShowConfirmationDialog(true)
diff --git a/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx b/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx
index 218c82d9ca521..1d9d98afe9d6c 100644
--- a/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx
+++ b/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx
@@ -50,6 +50,7 @@ import { InlineLink } from 'components/ui/InlineLink'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import * as z from 'zod'
+import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
const GITHUB_ICON = (