diff --git a/frontend/src/app/auth/login/LoginForm.tsx b/frontend/src/app/auth/login/LoginForm.tsx index 391e310..b2531c0 100644 --- a/frontend/src/app/auth/login/LoginForm.tsx +++ b/frontend/src/app/auth/login/LoginForm.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState, useEffect, useCallback } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; import Link from 'next/link'; import { useRouter, useSearchParams } from 'next/navigation'; import { Eye, EyeOff, Mail, Lock, ArrowRight, Loader2, Languages } from 'lucide-react'; @@ -31,6 +32,8 @@ export function LoginForm() { const { clientId: googleClientId, enabled: googleEnabled } = useGoogleConfig(); + const queryClient = useQueryClient(); + useEffect(() => { if (loginMutation.isError && loginMutation.error) { notify({ @@ -39,7 +42,7 @@ export function LoginForm() { variant: 'destructive', }); } - }, [loginMutation.isError, loginMutation.error, notify]); + }, [loginMutation.isError, loginMutation.error, notify, t]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -55,6 +58,7 @@ export function LoginForm() { { credential: credentialResponse.credential }, ); const { access_token, refresh_token } = response.data; + queryClient.clear(); localStorage.setItem('token', access_token); localStorage.setItem('refresh_token', refresh_token); router.push(redirect); @@ -67,7 +71,7 @@ export function LoginForm() { } finally { setGoogleLoading(false); } - }, [redirect, router, notify, t]); + }, [redirect, router, notify, t, queryClient]); const handleGoogleError = useCallback(() => { notify({ diff --git a/frontend/src/app/auth/login/useLogin.ts b/frontend/src/app/auth/login/useLogin.ts index 3085589..b3563d9 100644 --- a/frontend/src/app/auth/login/useLogin.ts +++ b/frontend/src/app/auth/login/useLogin.ts @@ -1,6 +1,6 @@ 'use client'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useRouter, useSearchParams } from 'next/navigation'; import { apiClient, ApiClientError } from '@/lib/apiClient'; import type { LoginRequest, LoginResponse } from './types'; @@ -9,6 +9,7 @@ export function useLogin() { const router = useRouter(); const searchParams = useSearchParams(); const redirect = searchParams.get('redirect') || '/dashboard'; + const queryClient = useQueryClient(); return useMutation({ mutationFn: async (credentials: LoginRequest) => { @@ -19,6 +20,7 @@ export function useLogin() { return response.data; }, onSuccess: (data) => { + queryClient.clear(); localStorage.setItem('token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); router.push(redirect); diff --git a/frontend/src/app/auth/register/useRegister.ts b/frontend/src/app/auth/register/useRegister.ts index 09e6638..1bbdbb7 100644 --- a/frontend/src/app/auth/register/useRegister.ts +++ b/frontend/src/app/auth/register/useRegister.ts @@ -1,6 +1,6 @@ 'use client'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useRouter, useSearchParams } from 'next/navigation'; import { apiClient } from '@/lib/apiClient'; import type { RegisterRequest, RegisterResponse } from './types'; @@ -15,6 +15,7 @@ export function useRegister() { const router = useRouter(); const searchParams = useSearchParams(); const redirect = searchParams.get('redirect') || '/dashboard'; + const queryClient = useQueryClient(); return useMutation({ mutationFn: async (data: RegisterRequest) => { @@ -27,6 +28,7 @@ export function useRegister() { return loginResponse.data; }, onSuccess: (data) => { + queryClient.clear(); localStorage.setItem('token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); router.push(redirect); diff --git a/frontend/src/app/checkout/success/page.tsx b/frontend/src/app/checkout/success/page.tsx index 93568e7..32ac2dc 100644 --- a/frontend/src/app/checkout/success/page.tsx +++ b/frontend/src/app/checkout/success/page.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import { useSearchParams, useRouter } from 'next/navigation'; +import { useQueryClient } from '@tanstack/react-query'; import { API_BASE } from '@/lib/config'; import { CheckCircle2, XCircle, RefreshCw } from 'lucide-react'; @@ -15,6 +16,7 @@ export default function CheckoutSuccessPage() { const searchParams = useSearchParams(); const router = useRouter(); const sessionId = searchParams.get('session_id'); + const queryClient = useQueryClient(); const [status, setStatus] = useState<'syncing' | 'ok' | 'error'>('syncing'); const [message, setMessage] = useState(''); @@ -42,6 +44,7 @@ export default function CheckoutSuccessPage() { if (res.ok) { setStatus('ok'); setMessage(data.data?.plan ? `Forfait ${data.data.plan} activé !` : 'Abonnement activé !'); + queryClient.invalidateQueries({ queryKey: ['user', 'me'] }); // Redirect after 2s setTimeout(() => router.replace('/dashboard/profile?tab=subscription'), 2000); } else { diff --git a/frontend/src/app/dashboard/profile/page.tsx b/frontend/src/app/dashboard/profile/page.tsx index bad8287..19a3332 100644 --- a/frontend/src/app/dashboard/profile/page.tsx +++ b/frontend/src/app/dashboard/profile/page.tsx @@ -16,6 +16,7 @@ import { ThemeToggle } from '@/components/ui/theme-toggle'; import { languages } from '@/lib/api'; import { useTranslationStore } from '@/lib/store'; import { cn } from '@/lib/utils'; +import { useQueryClient } from '@tanstack/react-query'; /* ── helpers ──────────────────────────────────────────────────── */ const PLAN_ICONS: Record = { @@ -90,6 +91,8 @@ export default function ProfilePage() { const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null; const authHeaders = { Authorization: `Bearer ${token}` }; + const queryClient = useQueryClient(); + const fetchData = useCallback(async () => { if (!token) { router.push('/auth/login?redirect=/dashboard/profile'); return; } try { @@ -97,10 +100,15 @@ export default function ProfilePage() { fetch(`${API_BASE}/api/v1/auth/me`, { headers: authHeaders }), fetch(`${API_BASE}/api/v1/auth/usage`, { headers: authHeaders }), ]); - if (meRes.ok) { const j = await meRes.json(); setUser(j.data ?? j); } + if (meRes.ok) { + const j = await meRes.json(); + const userData = j.data ?? j; + setUser(userData); + queryClient.setQueryData(['user', 'me'], userData); + } if (usageRes.ok) { const j = await usageRes.json(); setUsage(j.data ?? j); } } catch { /* ignore */ } finally { setLoading(false); } - }, [token]); // eslint-disable-line react-hooks/exhaustive-deps + }, [token, queryClient]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { fetchData(); }, [fetchData]); useEffect(() => { setDefaultLanguage(settings.defaultTargetLanguage); }, [settings.defaultTargetLanguage]); diff --git a/frontend/src/app/dashboard/useLogout.ts b/frontend/src/app/dashboard/useLogout.ts index 73743e1..9400a3b 100644 --- a/frontend/src/app/dashboard/useLogout.ts +++ b/frontend/src/app/dashboard/useLogout.ts @@ -1,14 +1,17 @@ 'use client'; import { useRouter } from 'next/navigation'; +import { useQueryClient } from '@tanstack/react-query'; export function useLogout() { const router = useRouter(); + const queryClient = useQueryClient(); const logout = () => { localStorage.removeItem('token'); localStorage.removeItem('refresh_token'); localStorage.removeItem('user'); + queryClient.clear(); router.push('/'); };