feat(admin): consentement aux annonces et désabonnement sans connexion
Les comptes déjà créés ne sont pas inscrits. Le choix est facultatif à l’inscription et dans les paramètres. Un lien public demande confirmation avant d’arrêter les actualités, sans toucher aux messages de compte.
This commit is contained in:
@@ -6,7 +6,8 @@ import { useLanguage } from '@/lib/i18n'
|
||||
import { updateAISettings } from '@/app/actions/ai-settings'
|
||||
import { toast } from 'sonner'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { Globe, Bell, Shield, Brain, HelpCircle } from 'lucide-react'
|
||||
import { Globe, Bell, Shield, Brain, HelpCircle, Mail } from 'lucide-react'
|
||||
import { updateMyMarketingPreference } from '@/app/actions/marketing-preference'
|
||||
import { motion } from 'motion/react'
|
||||
import { openCookiePreferences } from '@/lib/consent/cookie-consent'
|
||||
import { useAiConsent } from '@/components/legal/ai-consent-provider'
|
||||
@@ -21,10 +22,14 @@ interface GeneralSettingsClientProps {
|
||||
desktopNotifications: boolean
|
||||
autoSave: boolean
|
||||
}
|
||||
initialMarketing?: {
|
||||
optedIn: boolean
|
||||
blocked: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export function GeneralSettingsClient({ initialSettings }: GeneralSettingsClientProps) {
|
||||
const { t, setLanguage: setContextLanguage } = useLanguage()
|
||||
export function GeneralSettingsClient({ initialSettings, initialMarketing }: GeneralSettingsClientProps) {
|
||||
const { t, language: uiLanguage, setLanguage: setContextLanguage } = useLanguage()
|
||||
const { hasAiConsent, revokeConsent, requestAiConsent } = useAiConsent()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
@@ -45,6 +50,9 @@ export function GeneralSettingsClient({ initialSettings }: GeneralSettingsClient
|
||||
const [emailNotifications, setEmailNotifications] = useState(initialSettings.emailNotifications ?? false)
|
||||
const [desktopNotifications, setDesktopNotifications] = useState(initialSettings.desktopNotifications ?? false)
|
||||
const [autoSave, setAutoSave] = useState(initialSettings.autoSave ?? true)
|
||||
const [marketingOptIn, setMarketingOptIn] = useState(initialMarketing?.optedIn === true)
|
||||
const [marketingBlocked] = useState(initialMarketing?.blocked === true)
|
||||
const [marketingSaving, setMarketingSaving] = useState(false)
|
||||
|
||||
const handleLanguageChange = async (value: string) => {
|
||||
setLanguage(value)
|
||||
@@ -80,6 +88,20 @@ export function GeneralSettingsClient({ initialSettings }: GeneralSettingsClient
|
||||
toast.success(t('settings.settingsSaved'))
|
||||
}
|
||||
|
||||
const handleMarketingChange = async (enabled: boolean) => {
|
||||
if (marketingBlocked) return
|
||||
setMarketingSaving(true)
|
||||
setMarketingOptIn(enabled)
|
||||
const result = await updateMyMarketingPreference(enabled, uiLanguage)
|
||||
setMarketingSaving(false)
|
||||
if (!result.ok) {
|
||||
setMarketingOptIn(!enabled)
|
||||
toast.error(t(result.error === 'blocked' ? 'settings.marketingBlocked' : 'settings.settingsSaveFailed'))
|
||||
return
|
||||
}
|
||||
toast.success(t('settings.settingsSaved'))
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
@@ -222,6 +244,38 @@ export function GeneralSettingsClient({ initialSettings }: GeneralSettingsClient
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-xl p-8 space-y-6 md:col-span-2">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="p-3 bg-paper dark:bg-white/10 rounded-2xl text-concrete border border-border">
|
||||
<Mail size={18} />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-base font-bold text-ink">{t('settings.marketingTitle')}</h4>
|
||||
<p className="text-[11px] text-concrete max-w-2xl leading-relaxed">
|
||||
{t('settings.marketingDescription')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<label className={cn('relative inline-flex items-center shrink-0', marketingBlocked || marketingSaving ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer')}>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="sr-only peer"
|
||||
checked={marketingOptIn}
|
||||
disabled={marketingBlocked || marketingSaving}
|
||||
onChange={(e) => handleMarketingChange(e.target.checked)}
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-200 dark:bg-white/10 rounded-full peer peer-checked:after:translate-x-[20px] peer-checked:after:border-white after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all duration-300 ease-in-out peer-checked:bg-brand-accent" />
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-[12px] leading-relaxed text-ink/80">
|
||||
{t('settings.marketingLegal')}
|
||||
</p>
|
||||
{marketingBlocked && (
|
||||
<p className="text-[12px] text-rose-600">{t('settings.marketingBlocked')}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref={aiConsentSectionRef}
|
||||
className={cn(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { auth } from '@/auth'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getAISettings } from '@/app/actions/ai-settings'
|
||||
import { getMyMarketingPreference } from '@/app/actions/marketing-preference'
|
||||
import { GeneralSettingsClient } from './general-settings-client'
|
||||
|
||||
export default async function GeneralSettingsPage() {
|
||||
@@ -15,10 +16,15 @@ export default async function GeneralSettingsPage() {
|
||||
desktopNotifications,
|
||||
autoSave,
|
||||
} = await getAISettings()
|
||||
const marketing = await getMyMarketingPreference()
|
||||
|
||||
return (
|
||||
<GeneralSettingsClient
|
||||
initialSettings={{ preferredLanguage, emailNotifications, desktopNotifications, autoSave }}
|
||||
initialMarketing={{
|
||||
optedIn: marketing?.optedIn === true,
|
||||
blocked: marketing?.blocked === true,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user