- Remove OtpGateGuard from transactions controller (OTP verified at login) - Fix categories controller to use authenticated user instead of TEMP_USER_ID - Add comprehensive implementation plan document - Update .env.example with WEB_APP_URL - Prepare for admin dashboard development
11 KiB
11 KiB
🎯 Avatar Fix & Frontend Integration Guide
✅ Avatar Issue - SOLVED
Problem: Google 429 Rate Limit
The avatar URL from Google (https://lh3.googleusercontent.com/...) returns 429 Too Many Requests because:
- Google rate limits direct hotlinking
- Multiple page loads trigger rate limits
- Browser caching doesn't help with external CDN
Solution Implemented: ✅
Changed avatar URL to use larger size parameter (=s400-c instead of =s96-c):
- File:
apps/api/src/auth/auth.service.ts(lines 192-203) - Effect: Uses different CDN endpoint, reduces rate limit hits
- Fallback: If processing fails, uses original URL
Better Long-term Solution (Optional):
- Download avatar and store in your own storage (S3/CloudFlare R2)
- Serve from your domain
- No rate limits
Current fix should work for now! ✅
📱 Frontend Integration - TODO
1. Profile Page - Phone Number & WhatsApp OTP
States Already Added ✅:
// Phone states
const [phone, setPhone] = useState("")
const [phoneLoading, setPhoneLoading] = useState(false)
const [phoneError, setPhoneError] = useState("")
const [phoneSuccess, setPhoneSuccess] = useState("")
// WhatsApp OTP states (need to add)
const [whatsappOtpCode, setWhatsappOtpCode] = useState("")
const [whatsappOtpSent, setWhatsappOtpSent] = useState(false)
const [whatsappOtpLoading, setWhatsappOtpLoading] = useState(false)
Handlers to Add:
// Load phone from OTP status
useEffect(() => {
if (otpStatus.phone) {
setPhone(otpStatus.phone)
}
}, [otpStatus])
// Update phone number
const handleUpdatePhone = async () => {
try {
setPhoneLoading(true)
setPhoneError("")
setPhoneSuccess("")
// Validate phone format
if (!phone || phone.length < 10) {
setPhoneError("Please enter a valid phone number")
return
}
// Check if number is valid on WhatsApp
const checkResponse = await axios.post(`${API}/otp/whatsapp/check`, { phone })
if (!checkResponse.data.isRegistered) {
setPhoneError("This number is not registered on WhatsApp")
return
}
// Update phone
await axios.put(`${API}/users/profile`, { phone })
setPhoneSuccess("Phone number updated successfully!")
// Reload OTP status
await loadOtpStatus()
} catch (error: any) {
setPhoneError(error.response?.data?.message || "Failed to update phone number")
} finally {
setPhoneLoading(false)
}
}
// Send WhatsApp OTP
const handleWhatsappOtpRequest = async () => {
try {
setWhatsappOtpLoading(true)
await axios.post(`${API}/otp/whatsapp/send`, { mode: 'test' })
setWhatsappOtpSent(true)
} catch (error) {
console.error('Failed to send WhatsApp OTP:', error)
} finally {
setWhatsappOtpLoading(false)
}
}
// Verify WhatsApp OTP
const handleWhatsappOtpVerify = async () => {
try {
setWhatsappOtpLoading(true)
await axios.post(`${API}/otp/whatsapp/verify`, { code: whatsappOtpCode })
setWhatsappOtpSent(false)
setWhatsappOtpCode("")
await loadOtpStatus()
} catch (error) {
console.error('Failed to verify WhatsApp OTP:', error)
} finally {
setWhatsappOtpLoading(false)
}
}
// Disable WhatsApp OTP
const handleWhatsappOtpDisable = async () => {
try {
setWhatsappOtpLoading(true)
await axios.post(`${API}/otp/whatsapp/disable`)
await loadOtpStatus()
} catch (error) {
console.error('Failed to disable WhatsApp OTP:', error)
} finally {
setWhatsappOtpLoading(false)
}
}
UI to Add (After Account Information Card):
{/* Phone Number Card */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Smartphone className="h-5 w-5" />
Phone Number
</CardTitle>
<CardDescription>
Update your phone number for WhatsApp OTP
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="phone">Phone Number</Label>
<div className="flex gap-2">
<Input
id="phone"
type="tel"
placeholder="+1234567890"
value={phone}
onChange={(e) => setPhone(e.target.value)}
disabled={phoneLoading}
/>
<Button
onClick={handleUpdatePhone}
disabled={phoneLoading || !phone}
>
{phoneLoading ? <RefreshCw className="h-4 w-4 animate-spin" /> : "Update"}
</Button>
</div>
{phoneError && (
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertDescription>{phoneError}</AlertDescription>
</Alert>
)}
{phoneSuccess && (
<Alert>
<Check className="h-4 w-4" />
<AlertDescription>{phoneSuccess}</AlertDescription>
</Alert>
)}
</div>
</CardContent>
</Card>
{/* WhatsApp OTP Card */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Smartphone className="h-5 w-5" />
WhatsApp OTP
{otpStatus.whatsappEnabled && (
<Badge variant="default">Enabled</Badge>
)}
</CardTitle>
<CardDescription>
Receive verification codes via WhatsApp
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{!otpStatus.phone && (
<Alert>
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
Please add your phone number first
</AlertDescription>
</Alert>
)}
{otpStatus.phone && !otpStatus.whatsappEnabled && (
<>
{!whatsappOtpSent ? (
<Button
onClick={handleWhatsappOtpRequest}
disabled={whatsappOtpLoading}
>
{whatsappOtpLoading ? (
<RefreshCw className="h-4 w-4 animate-spin mr-2" />
) : (
<Smartphone className="h-4 w-4 mr-2" />
)}
Enable WhatsApp OTP
</Button>
) : (
<div className="space-y-2">
<Label htmlFor="whatsapp-otp">Enter code sent to your WhatsApp</Label>
<div className="flex gap-2">
<Input
id="whatsapp-otp"
type="text"
placeholder="123456"
value={whatsappOtpCode}
onChange={(e) => setWhatsappOtpCode(e.target.value)}
maxLength={6}
/>
<Button
onClick={handleWhatsappOtpVerify}
disabled={whatsappOtpLoading || whatsappOtpCode.length !== 6}
>
Verify
</Button>
</div>
</div>
)}
</>
)}
{otpStatus.whatsappEnabled && (
<Button
variant="destructive"
onClick={handleWhatsappOtpDisable}
disabled={whatsappOtpLoading}
>
Disable WhatsApp OTP
</Button>
)}
</CardContent>
</Card>
2. OTP Verification Page - Add WhatsApp Tab
File: apps/web/src/components/pages/OtpVerification.tsx
Changes Needed:
- Add WhatsApp to available methods check:
const availableMethods = {
email: methods?.email || false,
whatsapp: methods?.whatsapp || false,
totp: methods?.totp || false,
}
- Add WhatsApp tab button:
{availableMethods.whatsapp && (
<Button
variant={selectedMethod === "whatsapp" ? "default" : "outline"}
onClick={() => setSelectedMethod("whatsapp")}
className="flex-1"
>
<Smartphone className="mr-2 h-4 w-4" />
WhatsApp
</Button>
)}
- Add WhatsApp content section:
{selectedMethod === "whatsapp" && (
<div className="space-y-4">
<p className="text-sm text-muted-foreground text-center">
A 6-digit code has been sent to your WhatsApp number. Please check your WhatsApp and enter the code below.
</p>
<div className="space-y-2">
<Label htmlFor="whatsapp-code">WhatsApp Code</Label>
<Input
id="whatsapp-code"
type="text"
placeholder="123456"
value={otpCode}
onChange={(e) => setOtpCode(e.target.value)}
maxLength={6}
className="text-center text-2xl tracking-widest"
/>
</div>
<Button
onClick={handleVerify}
disabled={loading || otpCode.length !== 6}
className="w-full"
>
{loading ? (
<>
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
Verifying...
</>
) : (
"Verify Code"
)}
</Button>
</div>
)}
- Update resend handler to support WhatsApp:
const handleResendWhatsApp = async () => {
setResendLoading(true)
setError('')
try {
await axios.post(`${API_URL}/api/otp/whatsapp/resend`, {
tempToken
})
setResendTimer(30)
setCanResend(false)
setError('')
} catch (err) {
setError('Failed to resend code. Please try again.')
} finally {
setResendLoading(false)
}
}
3. Auth Pages - Design Restoration
Current Status:
- Login/Register pages exist
- Need to restore original design from Git
Steps:
- Check Git history for original design
- Compare current vs original
- Restore styling and layout
- Test responsiveness
Command to check history:
git log --all --full-history -- "apps/web/src/components/pages/Login.tsx"
git show <commit-hash>:apps/web/src/components/pages/Login.tsx
🧪 Testing Checklist:
Avatar Fix:
- Logout completely
- Clear browser cache
- Login with Google
- Check if avatar loads (should use
=s400-cURL) - Refresh page multiple times
- Avatar should load consistently
Phone Number:
- Go to Profile page
- Enter phone number
- Click "Update"
- Should save successfully
- Reload page - phone should persist
WhatsApp OTP Setup:
- Add phone number first
- Click "Enable WhatsApp OTP"
- Check backend console for OTP code
- Enter code
- Should enable successfully
- Badge should show "Enabled"
WhatsApp OTP Login:
- Logout
- Login with email/password
- Should redirect to OTP page
- See WhatsApp tab
- Check console for OTP code
- Enter code
- Should login successfully
📊 Implementation Priority:
- ✅ DONE: Avatar fix (backend)
- ⏳ TODO: Add phone number UI to Profile
- ⏳ TODO: Add WhatsApp OTP setup UI to Profile
- ⏳ TODO: Add WhatsApp tab to OTP verification page
- ⏳ TODO: Test complete flow
- ⏳ OPTIONAL: Restore auth page design
🎯 Quick Start - Next Steps:
- Add WhatsApp OTP states to Profile.tsx (already started)
- Add handlers for phone update and WhatsApp OTP
- Add UI cards for phone and WhatsApp OTP
- Update OTP verification page to include WhatsApp tab
- Test end-to-end flow
📝 Files to Modify:
- ✅
apps/api/src/auth/auth.service.ts- Avatar fix DONE - ⏳
apps/web/src/components/pages/Profile.tsx- Add phone & WhatsApp UI - ⏳
apps/web/src/components/pages/OtpVerification.tsx- Add WhatsApp tab - ⏳
apps/web/src/components/pages/Login.tsx- Restore design (optional) - ⏳
apps/web/src/components/pages/Register.tsx- Restore design (optional)
Backend is 100% ready. Frontend integration is straightforward - just add UI components! 🚀