feat: redirect to AI consent settings when consent is missing
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 7m27s
CI / Deploy production (on server) (push) Successful in 23s

- requestAiConsent now redirects to /settings/general?highlight=aiConsent
  instead of opening the modal when called outside the settings page
- Settings page highlights the consent section and scrolls to it
- Adds consent.ai.settingsBanner i18n key (fr/en)
- Keeps inline modal on /settings/general so the grant button still works
This commit is contained in:
Antigravity
2026-07-18 17:52:40 +00:00
parent 324bf40658
commit f385d43d5d
6 changed files with 81 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ import { useReducedMotion } from 'motion/react'
import { Inbox, Send, Bell, Mail } from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
import { useAiConsent } from '@/components/legal/ai-consent-provider'
import { redirectToAiConsentSettings } from '@/lib/consent/ai-consent-redirect'
import { createNote } from '@/app/actions/notes'
import { emitNoteChange } from '@/lib/note-change-sync'
import { toast } from 'sonner'
@@ -519,7 +520,10 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
try {
const res = await fetch('/api/ai/echo')
const json = await res.json()
if (res.status === 403 && json.code === 'ai_consent_required') { await requestAiConsent(); return }
if (res.status === 403 && json.code === 'ai_consent_required') {
redirectToAiConsentSettings(router)
return
}
if (!res.ok) throw new Error(json.error)
if (json.insight) {
await reloadBriefingAndPaths()
@@ -533,7 +537,7 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
} finally {
setEchoRefreshing(false)
}
}, [requestAiConsent, reloadBriefingAndPaths, t])
}, [requestAiConsent, reloadBriefingAndPaths, t, router])
const handleEnableAi = useCallback(async () => {
await requestAiConsent()

View File

@@ -2,7 +2,9 @@
import { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { getLocalStorageAiConsent, setLocalStorageAiConsent, removeLocalStorageAiConsent } from '@/lib/consent/ai-consent-client'
import { redirectToAiConsentSettings } from '@/lib/consent/ai-consent-redirect'
import { updateAISettings } from '@/app/actions/ai-settings'
import { AiConsentModal } from './ai-consent-modal'
import { toast } from 'sonner'
@@ -24,6 +26,7 @@ interface AiConsentProviderProps {
export function AiConsentProvider({ children, initialPersistentConsent = false }: AiConsentProviderProps) {
const { data: session, update: updateSession } = useSession()
const { t } = useLanguage()
const router = useRouter()
const [persistentConsent, setPersistentConsent] = useState(false)
const [sessionConsent, setSessionConsent] = useState(false)
@@ -60,11 +63,22 @@ export function AiConsentProvider({ children, initialPersistentConsent = false }
return Promise.resolve(true)
}
setModalOpen(true)
// On settings page we keep the inline modal; elsewhere redirect to settings
// so the user sees exactly where to grant consent.
if (typeof window !== 'undefined' && window.location.pathname.startsWith('/settings/general')) {
setModalOpen(true)
return new Promise<boolean>((resolve) => {
pendingResolveRef.current = resolve
})
}
redirectToAiConsentSettings(router, { replace: false })
return new Promise<boolean>((resolve) => {
pendingResolveRef.current = resolve
pendingResolveRef.current(false)
pendingResolveRef.current = null
})
}, [hasAiConsent])
}, [hasAiConsent, router])
const handleConfirm = async (remember: boolean) => {
setModalOpen(false)