Skip to content

Commit c469e4d

Browse files
committed
fix: address review -- unused import, React Query, redis import, email placeholder
1. Remove unused 'user_limits' import (CI lint failure) 2. Move redis_client import to module top, remove late import in function 3. Replace real email with admin@example.com in .env.example 4. Convert AdminPage from useEffect+fetch to React Query (useQuery) per CLAUDE.md: 'Use React Query for all server data fetching' - queryKey: ['admin', 'users'] - changeTier invalidates query instead of manual state update - refetch replaces fetchUsers callback Skipped: sync->async route conversion. Supabase client is sync, FastAPI correctly runs sync handlers in threadpool. Converting to async def with sync DB calls would block the event loop. flake8 passes. Build passes.
1 parent efd28e5 commit c469e4d

3 files changed

Lines changed: 19 additions & 28 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ VOYAGE_API_KEY=
7070

7171
# Admin access (comma-separated emails)
7272
# Users with these emails can access /api/v1/admin/* routes
73-
ADMIN_EMAILS=devanshurajesh@gmail.com
73+
ADMIN_EMAILS=admin@example.com

backend/routes/admin.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from fastapi import APIRouter, HTTPException, Depends
88
from pydantic import BaseModel
99

10-
from dependencies import user_limits
10+
from dependencies import redis_client
1111
from middleware.auth import require_auth, AuthContext
1212
from services.observability import logger
1313
from services.supabase_service import get_supabase_service
@@ -137,7 +137,6 @@ def update_user_tier(
137137
raise HTTPException(status_code=500, detail="Failed to update tier")
138138

139139
# Clear Redis cache so new tier takes effect immediately
140-
from dependencies import redis_client
141140
if redis_client:
142141
try:
143142
redis_client.delete(f"user:tier:{user_id}")

frontend/src/pages/AdminPage.tsx

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
* Admins can change any user's tier with one click.
66
*/
77

8-
import { useState, useEffect, useCallback } from 'react'
8+
import { useState } from 'react'
9+
import { useQuery, useQueryClient } from '@tanstack/react-query'
910
import { Shield, Users, ArrowUpCircle, ArrowDownCircle, Loader2, RefreshCw } from 'lucide-react'
1011
import { toast } from 'sonner'
1112
import { Button } from '@/components/ui/button'
@@ -33,33 +34,26 @@ const TIER_COLORS: Record<string, string> = {
3334

3435
export function AdminPage() {
3536
const { session } = useAuth()
36-
const [users, setUsers] = useState<AdminUser[]>([])
37-
const [loading, setLoading] = useState(true)
37+
const queryClient = useQueryClient()
3838
const [updating, setUpdating] = useState<string | null>(null)
39-
const [error, setError] = useState<string | null>(null)
4039

4140
const headers = { Authorization: `Bearer ${session?.access_token}` }
4241

43-
const fetchUsers = useCallback(async () => {
44-
try {
45-
setLoading(true)
46-
setError(null)
42+
const { data, isLoading: loading, error: queryError, refetch } = useQuery<{ users: AdminUser[] }>({
43+
queryKey: ['admin', 'users'],
44+
queryFn: async () => {
4745
const resp = await fetch(`${API_URL}/admin/users`, { headers })
4846
if (resp.status === 403) {
49-
setError('Admin access required. Your email is not in the ADMIN_EMAILS list.')
50-
return
47+
throw new Error('Admin access required. Your email is not in the ADMIN_EMAILS list.')
5148
}
5249
if (!resp.ok) throw new Error('Failed to fetch users')
53-
const data = await resp.json()
54-
setUsers(data.users || [])
55-
} catch (e) {
56-
setError(e instanceof Error ? e.message : 'Failed to load users')
57-
} finally {
58-
setLoading(false)
59-
}
60-
}, [session?.access_token])
50+
return resp.json()
51+
},
52+
enabled: !!session?.access_token,
53+
})
6154

62-
useEffect(() => { fetchUsers() }, [fetchUsers])
55+
const users = data?.users ?? []
56+
const error = queryError instanceof Error ? queryError.message : queryError ? 'Failed to load users' : null
6357

6458
async function changeTier(userId: string, email: string, newTier: string) {
6559
setUpdating(userId)
@@ -73,10 +67,8 @@ export function AdminPage() {
7367
const err = await resp.json().catch(() => ({}))
7468
throw new Error(err.detail || 'Failed to update tier')
7569
}
76-
const result = await resp.json()
77-
setUsers((prev) =>
78-
prev.map((u) => (u.id === userId ? { ...u, tier: result.tier } : u)),
79-
)
70+
await resp.json()
71+
queryClient.invalidateQueries({ queryKey: ['admin', 'users'] })
8072
toast.success(`Updated ${email} to ${newTier}`)
8173
} catch (e) {
8274
toast.error(e instanceof Error ? e.message : 'Update failed')
@@ -90,7 +82,7 @@ export function AdminPage() {
9082
<div className="flex flex-col items-center justify-center min-h-[400px] gap-4">
9183
<Shield className="w-12 h-12 text-destructive" />
9284
<p className="text-muted-foreground">{error}</p>
93-
<Button variant="outline" onClick={fetchUsers}>Retry</Button>
85+
<Button variant="outline" onClick={() => refetch()}>Retry</Button>
9486
</div>
9587
)
9688
}
@@ -107,7 +99,7 @@ export function AdminPage() {
10799
<p className="text-sm text-muted-foreground">User management and tier control</p>
108100
</div>
109101
</div>
110-
<Button variant="outline" onClick={fetchUsers} disabled={loading}>
102+
<Button variant="outline" onClick={() => refetch()} disabled={loading}>
111103
<RefreshCw className={cn('w-4 h-4 mr-2', loading && 'animate-spin')} />
112104
Refresh
113105
</Button>

0 commit comments

Comments
 (0)