diff --git a/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.test.tsx b/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.test.tsx index 1113bd5bf07..477a9229795 100644 --- a/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.test.tsx +++ b/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.test.tsx @@ -34,6 +34,8 @@ const createSnippet = (id: string, name: string): SnippetWithContent => ({ const SNIPPET_A = createSnippet('snippet-a', 'First query') const SNIPPET_B = createSnippet('snippet-b', 'Second query') +const UNTITLED_A = createSnippet('untitled-a', 'Untitled query') +const UNTITLED_B = createSnippet('untitled-b', 'Untitled query') /** The modal renders the AI title generator, which checks for an OpenAI key when self-hosted. */ const mockOpenAIKeyCheck = () => @@ -108,6 +110,47 @@ describe('RenameQueryModal', () => { expect(screen.getByRole('button', { name: 'Rename query' })).toBeDisabled() }) + test('resets for the next snippet when both snippets share a name', async () => { + mockOpenAIKeyCheck() + const requests = mockUpsert() + const onComplete = vi.fn() + + const { rerender } = customRender( + + ) + + await userEvent.clear(getNameInput()) + await userEvent.type(getNameInput(), 'Renamed query') + fireEvent.click(screen.getByRole('button', { name: 'Rename query' })) + await waitFor(() => expect(onComplete).toHaveBeenCalledOnce()) + + // The parent keeps the renamed snippet selected while closing the modal, then reopens it for + // a second snippet that still carries the same original name + rerender( + + ) + rerender( + + ) + + await waitFor(() => expect(getNameInput()).toHaveValue('Untitled query')) + + await userEvent.clear(getNameInput()) + await userEvent.type(getNameInput(), 'Second renamed query') + fireEvent.click(screen.getByRole('button', { name: 'Rename query' })) + + await waitFor(() => expect(onComplete).toHaveBeenCalledTimes(2)) + expect(requests).toEqual([ + { id: 'untitled-a', name: 'Renamed query' }, + { id: 'untitled-b', name: 'Second renamed query' }, + ]) + }) + test('discards an abandoned edit when cancelled', async () => { mockOpenAIKeyCheck() const onCancel = vi.fn() diff --git a/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx b/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx index 1ae91dc20c1..8041ebe9b7d 100644 --- a/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx +++ b/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx @@ -49,12 +49,13 @@ const formSchema = z.object({ description: z.string().optional(), }) -export const RenameQueryModal = ({ - snippet = {} as any, - visible, - onCancel, - onComplete, -}: RenameQueryModalProps) => { +interface RenameQueryFormProps { + snippet: SqlSnippet | Snippet + onCancel: () => void + onComplete: () => void +} + +const RenameQueryForm = ({ snippet, onCancel, onComplete }: RenameQueryFormProps) => { const { ref } = useParams() const router = useRouter() @@ -145,7 +146,7 @@ export const RenameQueryModal = ({ } toast.success('Successfully renamed snippet!') - reset({ name, description }, { keepDirtyValues: false }) + reset({ name, description }) if (onComplete) onComplete() } catch (error: any) { // [Joshen] We probably need some rollback cause all the saving is async @@ -156,98 +157,105 @@ export const RenameQueryModal = ({ const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { name: name ?? '', description: description ?? '' }, - values: { name: name ?? '', description: description ?? '' }, - resetOptions: { keepDirtyValues: true }, }) const { reset, formState } = form const { isDirty, isSubmitting } = formState - const handleCancel = () => { - onCancel() - reset(undefined, { keepDirtyValues: false }) - } - return ( - +
+ + + ( + + + + + + )} + /> +
+ generateTitle()} + size="tiny" + disabled={ + isTitleGenerationLoading || !isApiKeySet || isHipaaProjectDisallowed || isAiOptedOut + } + 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.', + }, + }} + > +
+
+ +
+ Rename with Supabase AI +
+
+
+ ( + + +