useAIOptInForm.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { zodResolver } from '@hookform/resolvers/zod'
  2. import { PermissionAction } from '@supabase/shared-types/out/constants'
  3. import { useQueryClient } from '@tanstack/react-query'
  4. import { LOCAL_STORAGE_KEYS } from 'common'
  5. import { useForm } from 'react-hook-form'
  6. import { toast } from 'sonner'
  7. import * as z from 'zod'
  8. import { useOrganizationUpdateMutation } from '@/data/organizations/organization-update-mutation'
  9. import { invalidateOrganizationsQuery } from '@/data/organizations/organizations-query'
  10. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  11. import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
  12. import { getAiOptInLevel } from '@/hooks/misc/useOrgOptedIntoAi'
  13. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  14. import { OPT_IN_TAGS } from '@/lib/constants'
  15. import type { ResponseError } from '@/types'
  16. // Shared schema definition
  17. export const AIOptInSchema = z.object({
  18. aiOptInLevel: z.enum(['disabled', 'schema', 'schema_and_log', 'schema_and_log_and_data'], {
  19. required_error: 'AI Opt-in level selection is required',
  20. }),
  21. })
  22. export type AIOptInFormValues = z.infer<typeof AIOptInSchema>
  23. /**
  24. * Hook to manage the AI Opt-In form state and submission logic.
  25. * Optionally takes an onSuccess callback (e.g., to close a modal).
  26. */
  27. export const useAIOptInForm = (onSuccessCallback?: () => void) => {
  28. const queryClient = useQueryClient()
  29. const { data: selectedOrganization } = useSelectedOrganizationQuery()
  30. const { can: canUpdateOrganization } = useAsyncCheckPermissions(
  31. PermissionAction.UPDATE,
  32. 'organizations'
  33. )
  34. const [, setUpdatedOptInSinceMCP] = useLocalStorageQuery(
  35. LOCAL_STORAGE_KEYS.AI_ASSISTANT_MCP_OPT_IN,
  36. false
  37. )
  38. const { mutate: updateOrganization, isPending: isUpdating } = useOrganizationUpdateMutation()
  39. const form = useForm<AIOptInFormValues>({
  40. resolver: zodResolver(AIOptInSchema as any),
  41. defaultValues: {
  42. aiOptInLevel: getAiOptInLevel(selectedOrganization?.opt_in_tags),
  43. },
  44. })
  45. const onSubmit = async (values: AIOptInFormValues) => {
  46. if (!canUpdateOrganization) {
  47. return toast.error('You do not have the required permissions to update this organization')
  48. }
  49. if (!selectedOrganization?.slug) {
  50. console.error('Organization slug is required')
  51. return toast.error('Failed to update settings: Organization not found.')
  52. }
  53. const existingOptInTags = selectedOrganization?.opt_in_tags ?? []
  54. let updatedOptInTags = existingOptInTags.filter(
  55. (tag: string) =>
  56. tag !== OPT_IN_TAGS.AI_SQL &&
  57. tag !== (OPT_IN_TAGS.AI_DATA ?? 'AI_DATA') &&
  58. tag !== (OPT_IN_TAGS.AI_LOG ?? 'AI_LOG')
  59. )
  60. if (
  61. values.aiOptInLevel === 'schema' ||
  62. values.aiOptInLevel === 'schema_and_log' ||
  63. values.aiOptInLevel === 'schema_and_log_and_data'
  64. ) {
  65. updatedOptInTags.push(OPT_IN_TAGS.AI_SQL)
  66. }
  67. if (
  68. values.aiOptInLevel === 'schema_and_log' ||
  69. values.aiOptInLevel === 'schema_and_log_and_data'
  70. ) {
  71. updatedOptInTags.push(OPT_IN_TAGS.AI_LOG)
  72. }
  73. if (values.aiOptInLevel === 'schema_and_log_and_data') {
  74. updatedOptInTags.push(OPT_IN_TAGS.AI_DATA)
  75. }
  76. updatedOptInTags = [...new Set(updatedOptInTags)]
  77. updateOrganization(
  78. { slug: selectedOrganization.slug, opt_in_tags: updatedOptInTags },
  79. {
  80. onSuccess: () => {
  81. invalidateOrganizationsQuery(queryClient)
  82. toast.success('Successfully updated AI opt-in settings')
  83. setUpdatedOptInSinceMCP(true)
  84. onSuccessCallback?.() // Call optional callback on success
  85. },
  86. onError: (error: ResponseError) => {
  87. toast.error(`Failed to update settings: ${error.message}`)
  88. },
  89. }
  90. )
  91. }
  92. return {
  93. form,
  94. onSubmit,
  95. isUpdating,
  96. currentOptInLevel: getAiOptInLevel(selectedOrganization?.opt_in_tags),
  97. }
  98. }