Files
supabase/apps/studio/components/ui/EditorPanel/SaveSnippetDialog.tsx
Ivan Vasilov f9b4ee871a fix: Disable generate snippet title feature when the org has disabled AI features (#46959)
This PR disabled generating snippet titles when running and disables the
"generate titles" buttons in Rename Snippet and Save Snippet dialogs
(which is accessed through the side SQL Editor).

How to test:
1. Disable AI for an org.
2. Try to run a new snippet, it shouldn't be renamed automatically.
3. Right click it, click Rename. The "generate title" in the dialog
should be disabled with a reason in a tooltip.
4. Open the side SQL Editor, write "select 1", click Save snippet. The
"generate title" in the dialog should be disabled.

Testing the same flows for HIPAA projects should say `This feature is
not available for HIPAA projects.`

<img width="715" height="833" alt="Screenshot 2026-06-15 at 23 02 15"
src="https://github.com/user-attachments/assets/f9b68f2f-5a5a-4a66-bd0d-9245f4e2f78e"
/>
<img width="948" height="845" alt="Screenshot 2026-06-15 at 23 02 01"
src="https://github.com/user-attachments/assets/b0c9a90e-6cc1-4262-a246-88617cec41dc"
/>



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

## Summary by CodeRabbit

* **New Features**
* AI feature availability is now gated by organization-level AI opt-in
settings instead of subscription-based HIPAA add-ons.

* **Bug Fixes**
* Updated "Generate with AI" buttons to display disabled state with
contextual messaging (missing API key, organization AI opt-out, HIPAA
project restriction, or generation in progress).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-16 00:21:40 +02:00

119 lines
3.8 KiB
TypeScript

import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import {
AiIconAnimation,
Button,
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogSection,
DialogSectionSeparator,
DialogTitle,
Input,
Label,
} from 'ui'
import { generateSnippetTitle } from '@/components/interfaces/SQLEditor/SQLEditor.constants'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import { useCheckOpenAIKeyQuery } from '@/data/ai/check-api-key-query'
import { useSqlTitleGenerateMutation } from '@/data/ai/sql-title-mutation'
import { useOrgAiOptInLevel } from '@/hooks/misc/useOrgOptedIntoAi'
interface SaveSnippetDialogProps {
open: boolean
sql: string
onOpenChange: (open: boolean) => void
onSave: (name: string) => void
}
export const SaveSnippetDialog = ({ open, sql, onOpenChange, onSave }: SaveSnippetDialogProps) => {
const { data: check } = useCheckOpenAIKeyQuery()
const [name, setName] = useState(generateSnippetTitle())
const isApiKeySet = !!check?.hasKey
// Orgs on HIPAA plans or that have disabled AI should not have access to Supabase AI
const { aiOptInLevel, isHipaaProjectDisallowed } = useOrgAiOptInLevel()
const isAiOptedOut = aiOptInLevel === 'disabled'
const { mutate: generateTitle, isPending: isGenerating } = useSqlTitleGenerateMutation({
onSuccess: ({ title }) => setName(title),
onError: (error) => toast.error(`Failed to generate title: ${error.message}`),
})
// Reset the name each time the dialog opens
useEffect(() => {
if (open) setName(generateSnippetTitle())
}, [open])
const handleSave = () => {
const trimmed = name.trim()
if (!trimmed) return
onSave(trimmed)
onOpenChange(false)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent size="small">
<DialogHeader>
<DialogTitle>Save snippet</DialogTitle>
</DialogHeader>
<DialogSectionSeparator />
<DialogSection className="flex flex-col gap-y-4 py-5">
<div className="flex flex-col gap-y-2">
<Label htmlFor="snippet-name">Name</Label>
<Input
id="snippet-name"
autoFocus
value={name}
onChange={(e) => setName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') handleSave()
}}
/>
</div>
<div className="flex justify-end">
<ButtonTooltip
type="default"
size="tiny"
disabled={isGenerating || !isApiKeySet || isHipaaProjectDisallowed || isAiOptedOut}
onClick={() => generateTitle({ sql })}
tooltip={{
content: {
side: 'bottom',
text: isHipaaProjectDisallowed
? 'This feature is not available for HIPAA projects.'
: isAiOptedOut
? 'Your organization has opted out of AI features.'
: isApiKeySet
? undefined
: 'Add your "OPENAI_API_KEY" to your environment variables to use this feature.',
},
}}
>
<div className="flex items-center gap-1">
<div className="scale-75">
<AiIconAnimation loading={isGenerating} />
</div>
<span>Generate with AI</span>
</div>
</ButtonTooltip>
</div>
</DialogSection>
<DialogSectionSeparator />
<DialogFooter className="px-5 py-4">
<Button type="default" onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button disabled={!name.trim()} onClick={handleSave}>
Save snippet
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}