diff --git a/src/app/(agent)/agent/dashboard/page.tsx b/src/app/(agent)/agent/dashboard/page.tsx index 2413be4..55fdff0 100644 --- a/src/app/(agent)/agent/dashboard/page.tsx +++ b/src/app/(agent)/agent/dashboard/page.tsx @@ -297,11 +297,19 @@ export default function AgentDashboard() { const handleSubmitVerification = async () => { setIsSubmittingVerification(true); try { - await usersService.submitForVerification(); - // Update local state + const result = await usersService.submitForVerification(); + console.log('Verification submit result:', result); + // Update local state immediately if (agentProfile) { setAgentProfile({ ...agentProfile, verificationStatus: 'PENDING_REVIEW', verificationNote: null } as AgentProfile); } + // Also re-fetch profile to confirm + try { + const freshProfile = await agentsService.getProfile(); + setAgentProfile(freshProfile); + } catch { + // Local state already updated + } } catch (err: any) { const data = err?.response?.data; if (data?.missingFields && Array.isArray(data.missingFields)) { @@ -328,18 +336,9 @@ export default function AgentDashboard() {

Reason: {agentProfile.verificationNote}

)}

Please update your profile and documents, then resubmit for verification.

-
- - Edit Profile - - -
+ + Edit & Resubmit + @@ -352,24 +351,15 @@ export default function AgentDashboard() { )} - {agentProfile?.verificationStatus === 'NONE' && ( + {(!agentProfile?.verificationStatus || agentProfile?.verificationStatus === 'NONE') && (
Info
-

Complete your profile and upload verification documents to get verified.

-
- - Complete Profile - - -
+

Complete your profile to get verified.

+ + Complete Profile +
diff --git a/src/app/(agent)/agent/edit/page.tsx b/src/app/(agent)/agent/edit/page.tsx index 855bf23..386a43e 100644 --- a/src/app/(agent)/agent/edit/page.tsx +++ b/src/app/(agent)/agent/edit/page.tsx @@ -7,6 +7,7 @@ import DynamicSection from './components/DynamicSection'; import RepeatableSection, { RepeatableEntryData } from './components/RepeatableSection'; import { profileSectionsService, ProfileSection, AgentTypeSectionsResponse } from '@/services/profile-sections.service'; import { agentsService, AgentProfile, FieldValueInput } from '@/services/agents.service'; +import { usersService } from '@/services/users.service'; import { MobileBackButton } from '@/components/layout/MobileBackButton'; export default function EditProfilePage() { @@ -38,6 +39,12 @@ export default function EditProfilePage() { const profile = await agentsService.getMyProfile(); setAgentProfile(profile); + // Block editing when verification is under review + if (profile.verificationStatus === 'PENDING_REVIEW') { + router.push('/agent/dashboard'); + return; + } + if (!profile.agentTypeId) { setError('Your profile does not have an agent type assigned. Please contact support.'); setLoading(false); @@ -276,28 +283,31 @@ export default function EditProfilePage() { await agentsService.saveFieldValues(fieldValues); // Also update main profile fields (phone, email) to keep them in sync - // This ensures that when the view page checks agentProfile.phone first, - // it gets the correct (possibly empty) value const profileUpdates: Partial<{ phone: string | null; email: string | null }> = {}; - // Sync phone field - check dynamic field slugs FIRST, then legacy 'phone' - // Dynamic fields (phone_number, etc.) are the source of truth from the form - // 'phone' is a legacy pre-populated key and should only be used as fallback const phoneFieldSlugs = ['phone_number', 'cell_number', 'office_number', 'phone']; for (const slug of phoneFieldSlugs) { const phoneValue = formData[slug]; if (phoneValue !== undefined) { - // Set to null if empty, otherwise use the value profileUpdates.phone = phoneValue === '' ? null : (phoneValue as string); - break; // Use the first found phone field + break; } } - // Only update if there are changes to sync if (Object.keys(profileUpdates).length > 0) { await agentsService.updateProfile(profileUpdates); } + // Auto-submit for verification (status: NONE or REJECTED → PENDING_REVIEW) + const status = agentProfile?.verificationStatus; + if (!status || status === 'NONE' || status === 'REJECTED') { + try { + await usersService.submitForVerification(); + } catch { + // If submit fails (e.g. already pending), still proceed + } + } + router.push('/agent/dashboard'); } catch (err) { console.error('Failed to save:', err); @@ -444,7 +454,7 @@ export default function EditProfilePage() { {isSaving && (
)} - {isSaving ? 'Saving...' : 'Save'} + {isSaving ? 'Submitting...' : 'Submit for Review'} )} diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 850dca9..da8b7d4 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -1,13 +1,14 @@ 'use client'; import { useState, useEffect } from 'react'; -import { useRouter } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import { signIn } from 'next-auth/react'; import Link from 'next/link'; import { resetLogoutState } from '@/services/api'; export default function LoginPage() { const router = useRouter(); + const searchParams = useSearchParams(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); @@ -19,7 +20,12 @@ export default function LoginPage() { useEffect(() => { localStorage.removeItem('isLoggingOut'); resetLogoutState(); - }, []); + // Show error from social login redirect + const errorParam = searchParams.get('error'); + if (errorParam) { + setError(errorParam); + } + }, [searchParams]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -90,6 +96,8 @@ export default function LoginPage() { }; const handleSocialLogin = (provider: 'google' | 'facebook' | 'twitter') => { + // Set login mode so backend rejects if user doesn't exist + document.cookie = `socialAuthMode=login;path=/;max-age=300;SameSite=Lax`; signIn(provider, { callbackUrl: '/' }); }; diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 14a6fc5..57bff52 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -99,7 +99,8 @@ export default function SignUpPage() { setError('Please select an agent type before signing up'); return; } - // Set cookies with selected role and agent type so server-side auth callback can read them + // Set cookies with mode, role and agent type so server-side auth callback can read them + document.cookie = `socialAuthMode=signup;path=/;max-age=300;SameSite=Lax`; document.cookie = `socialAuthRole=${userType};path=/;max-age=300;SameSite=Lax`; if (userType === 'AGENT' && selectedAgentTypeId) { document.cookie = `socialAuthAgentTypeId=${selectedAgentTypeId};path=/;max-age=300;SameSite=Lax`; diff --git a/src/auth.ts b/src/auth.ts index 25505c2..6f7f57e 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -117,11 +117,13 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ // Read role and agent type from cookies (set by signup page before OAuth redirect) let role: string | undefined; let agentTypeId: string | undefined; + let mode: string | undefined; try { const { cookies } = await import("next/headers"); const cookieStore = await cookies(); role = cookieStore.get("socialAuthRole")?.value; agentTypeId = cookieStore.get("socialAuthAgentTypeId")?.value; + mode = cookieStore.get("socialAuthMode")?.value; } catch { // Cookie may not be available } @@ -139,6 +141,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ avatar: user.image, role, agentTypeId, + mode: mode || 'signup', }), } ); @@ -150,6 +153,9 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ (user as any).role = data.data.user.role; (user as any).accessToken = data.data.accessToken; (user as any).refreshToken = data.data.refreshToken; + } else if (!res.ok) { + const errorMsg = data?.message || "Social login failed"; + return `/login?error=${encodeURIComponent(errorMsg)}`; } } catch (error) { console.error("Social auth sync error:", error);