import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import { LOCAL_STORAGE_KEYS } from 'common' import { useForm } from 'react-hook-form' import { toast } from 'sonner' import * as z from 'zod' import { useOrganizationUpdateMutation } from '@/data/organizations/organization-update-mutation' import { invalidateOrganizationsQuery } from '@/data/organizations/organizations-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage' import { getAiOptInLevel } from '@/hooks/misc/useOrgOptedIntoAi' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { OPT_IN_TAGS } from '@/lib/constants' import type { ResponseError } from '@/types' // Shared schema definition export const AIOptInSchema = z.object({ aiOptInLevel: z.enum(['disabled', 'schema', 'schema_and_log', 'schema_and_log_and_data'], { required_error: 'AI Opt-in level selection is required', }), }) export type AIOptInFormValues = z.infer /** * Hook to manage the AI Opt-In form state and submission logic. * Optionally takes an onSuccess callback (e.g., to close a modal). */ export const useAIOptInForm = (onSuccessCallback?: () => void) => { const queryClient = useQueryClient() const { data: selectedOrganization } = useSelectedOrganizationQuery() const { can: canUpdateOrganization } = useAsyncCheckPermissions( PermissionAction.UPDATE, 'organizations' ) const [, setUpdatedOptInSinceMCP] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.AI_ASSISTANT_MCP_OPT_IN, false ) const { mutate: updateOrganization, isPending: isUpdating } = useOrganizationUpdateMutation() const form = useForm({ resolver: zodResolver(AIOptInSchema as any), defaultValues: { aiOptInLevel: getAiOptInLevel(selectedOrganization?.opt_in_tags), }, }) const onSubmit = async (values: AIOptInFormValues) => { if (!canUpdateOrganization) { return toast.error('You do not have the required permissions to update this organization') } if (!selectedOrganization?.slug) { console.error('Organization slug is required') return toast.error('Failed to update settings: Organization not found.') } const existingOptInTags = selectedOrganization?.opt_in_tags ?? [] let updatedOptInTags = existingOptInTags.filter( (tag: string) => tag !== OPT_IN_TAGS.AI_SQL && tag !== (OPT_IN_TAGS.AI_DATA ?? 'AI_DATA') && tag !== (OPT_IN_TAGS.AI_LOG ?? 'AI_LOG') ) if ( values.aiOptInLevel === 'schema' || values.aiOptInLevel === 'schema_and_log' || values.aiOptInLevel === 'schema_and_log_and_data' ) { updatedOptInTags.push(OPT_IN_TAGS.AI_SQL) } if ( values.aiOptInLevel === 'schema_and_log' || values.aiOptInLevel === 'schema_and_log_and_data' ) { updatedOptInTags.push(OPT_IN_TAGS.AI_LOG) } if (values.aiOptInLevel === 'schema_and_log_and_data') { updatedOptInTags.push(OPT_IN_TAGS.AI_DATA) } updatedOptInTags = [...new Set(updatedOptInTags)] updateOrganization( { slug: selectedOrganization.slug, opt_in_tags: updatedOptInTags }, { onSuccess: () => { invalidateOrganizationsQuery(queryClient) toast.success('Successfully updated AI opt-in settings') setUpdatedOptInSinceMCP(true) onSuccessCallback?.() // Call optional callback on success }, onError: (error: ResponseError) => { toast.error(`Failed to update settings: ${error.message}`) }, } ) } return { form, onSubmit, isUpdating, currentOptInLevel: getAiOptInLevel(selectedOrganization?.opt_in_tags), } }