fix: Settings functionality + Dashboard tab counts
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
SETTINGS FIXES: - Profile save now calls real API (PUT /auth/me) - Updates store after successful profile save - Notification preferences now functional with state - Save preferences button added - Preferences stored in localStorage - Proper loading/saving states DASHBOARD FIX: - Portfolio data loads on mount (not just when tab active) - Tab counts now show correct numbers - Both Watchlist and Portfolio counts visible immediately API: - Added updateMe() method in api.ts
This commit is contained in:
@ -111,11 +111,12 @@ export default function DashboardPage() {
|
||||
}
|
||||
}, [isLoading, isAuthenticated, router])
|
||||
|
||||
// Load portfolio data on mount (so we can show count in tab)
|
||||
useEffect(() => {
|
||||
if (isAuthenticated && activeTab === 'portfolio') {
|
||||
if (isAuthenticated) {
|
||||
loadPortfolio()
|
||||
}
|
||||
}, [isAuthenticated, activeTab])
|
||||
}, [isAuthenticated])
|
||||
|
||||
const handleOpenBillingPortal = async () => {
|
||||
try {
|
||||
|
||||
@ -43,6 +43,14 @@ export default function SettingsPage() {
|
||||
email: '',
|
||||
})
|
||||
|
||||
// Notification preferences (local state - would be persisted via API in production)
|
||||
const [notificationPrefs, setNotificationPrefs] = useState({
|
||||
domain_availability: true,
|
||||
price_alerts: true,
|
||||
weekly_digest: false,
|
||||
})
|
||||
const [savingNotifications, setSavingNotifications] = useState(false)
|
||||
|
||||
// Price alerts
|
||||
const [priceAlerts, setPriceAlerts] = useState<PriceAlert[]>([])
|
||||
const [loadingAlerts, setLoadingAlerts] = useState(false)
|
||||
@ -92,9 +100,10 @@ export default function SettingsPage() {
|
||||
setSuccess(null)
|
||||
|
||||
try {
|
||||
// In a real app, this would call an API to update the user
|
||||
// For now, we'll just show success
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
await api.updateMe({ name: profileForm.name || undefined })
|
||||
// Update store with new user info
|
||||
const { checkAuth } = useStore.getState()
|
||||
await checkAuth()
|
||||
setSuccess('Profile updated successfully')
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to update profile')
|
||||
@ -103,6 +112,32 @@ export default function SettingsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveNotifications = async () => {
|
||||
setSavingNotifications(true)
|
||||
setError(null)
|
||||
setSuccess(null)
|
||||
|
||||
try {
|
||||
// Store in localStorage for now (would be API in production)
|
||||
localStorage.setItem('notification_prefs', JSON.stringify(notificationPrefs))
|
||||
setSuccess('Notification preferences saved')
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to save preferences')
|
||||
} finally {
|
||||
setSavingNotifications(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Load notification preferences from localStorage
|
||||
useEffect(() => {
|
||||
const saved = localStorage.getItem('notification_prefs')
|
||||
if (saved) {
|
||||
try {
|
||||
setNotificationPrefs(JSON.parse(saved))
|
||||
} catch {}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleDeletePriceAlert = async (tld: string, alertId: number) => {
|
||||
setDeletingAlertId(alertId)
|
||||
try {
|
||||
@ -301,7 +336,12 @@ export default function SettingsPage() {
|
||||
<p className="text-body-sm font-medium text-foreground">Domain Availability</p>
|
||||
<p className="text-body-xs text-foreground-muted">Get notified when watched domains become available</p>
|
||||
</div>
|
||||
<input type="checkbox" defaultChecked className="w-5 h-5 accent-accent" />
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationPrefs.domain_availability}
|
||||
onChange={(e) => setNotificationPrefs({ ...notificationPrefs, domain_availability: e.target.checked })}
|
||||
className="w-5 h-5 accent-accent cursor-pointer"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="flex items-center justify-between p-4 bg-background border border-border rounded-xl cursor-pointer hover:border-foreground/20 transition-colors">
|
||||
@ -309,7 +349,12 @@ export default function SettingsPage() {
|
||||
<p className="text-body-sm font-medium text-foreground">Price Alerts</p>
|
||||
<p className="text-body-xs text-foreground-muted">Get notified when TLD prices change</p>
|
||||
</div>
|
||||
<input type="checkbox" defaultChecked className="w-5 h-5 accent-accent" />
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationPrefs.price_alerts}
|
||||
onChange={(e) => setNotificationPrefs({ ...notificationPrefs, price_alerts: e.target.checked })}
|
||||
className="w-5 h-5 accent-accent cursor-pointer"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="flex items-center justify-between p-4 bg-background border border-border rounded-xl cursor-pointer hover:border-foreground/20 transition-colors">
|
||||
@ -317,9 +362,24 @@ export default function SettingsPage() {
|
||||
<p className="text-body-sm font-medium text-foreground">Weekly Digest</p>
|
||||
<p className="text-body-xs text-foreground-muted">Receive a weekly summary of your portfolio</p>
|
||||
</div>
|
||||
<input type="checkbox" className="w-5 h-5 accent-accent" />
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationPrefs.weekly_digest}
|
||||
onChange={(e) => setNotificationPrefs({ ...notificationPrefs, weekly_digest: e.target.checked })}
|
||||
className="w-5 h-5 accent-accent cursor-pointer"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleSaveNotifications}
|
||||
disabled={savingNotifications}
|
||||
className="mt-5 px-6 py-3 bg-foreground text-background text-ui font-medium rounded-xl
|
||||
hover:bg-foreground/90 disabled:opacity-50 transition-all flex items-center gap-2"
|
||||
>
|
||||
{savingNotifications ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
|
||||
Save Preferences
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Active Price Alerts */}
|
||||
|
||||
@ -130,6 +130,19 @@ class ApiClient {
|
||||
}>('/auth/me')
|
||||
}
|
||||
|
||||
async updateMe(data: { name?: string }) {
|
||||
return this.request<{
|
||||
id: number
|
||||
email: string
|
||||
name: string | null
|
||||
is_active: boolean
|
||||
created_at: string
|
||||
}>('/auth/me', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
}
|
||||
|
||||
// Password Reset
|
||||
async forgotPassword(email: string) {
|
||||
return this.request<{ message: string; success: boolean }>('/auth/forgot-password', {
|
||||
|
||||
Reference in New Issue
Block a user