Fix first-login error, awards performance, filter animation, cache invalidation, and query fixes

- Guard onboarding tRPC queries with session hydration check (fixes UNAUTHORIZED on first login)
- Defer expensive queries on awards page until UI elements are opened (dialog/tab)
- Fix perPage: 500 exceeding backend Zod max of 100 on awards eligibility query
- Add smooth open/close animation to project filters collapsible bar
- Fix seeded user status from ACTIVE to INVITED in seed-candidatures.ts
- Add router.refresh() cache invalidation across ~22 admin forms
- Fix geographic analytics query to use programId instead of round.programId
- Fix dashboard queries to scope by programId correctly
- Fix project.listPool and round queries for projects outside round context
- Add rounds page useEffect for state sync after mutations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-10 21:21:54 +01:00
parent 573785e440
commit 5cae78fe0c
26 changed files with 830 additions and 341 deletions

View File

@@ -2,6 +2,7 @@
import { useState, useMemo, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { useSession } from 'next-auth/react'
import { trpc } from '@/lib/trpc/client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
@@ -45,6 +46,8 @@ type Step = 'name' | 'photo' | 'country' | 'bio' | 'phone' | 'tags' | 'preferenc
export default function OnboardingPage() {
const router = useRouter()
const { data: session, status: sessionStatus } = useSession()
const isAuthenticated = sessionStatus === 'authenticated'
const [step, setStep] = useState<Step>('name')
const [initialized, setInitialized] = useState(false)
@@ -59,9 +62,15 @@ export default function OnboardingPage() {
'EMAIL' | 'WHATSAPP' | 'BOTH' | 'NONE'
>('EMAIL')
// Fetch current user data to get admin-preset tags
const { data: userData, isLoading: userLoading, refetch: refetchUser } = trpc.user.me.useQuery()
const { data: avatarUrl } = trpc.avatar.getUrl.useQuery()
// Fetch current user data only after session is hydrated
const { data: userData, isLoading: userLoading, refetch: refetchUser } = trpc.user.me.useQuery(
undefined,
{ enabled: isAuthenticated }
)
const { data: avatarUrl } = trpc.avatar.getUrl.useQuery(
undefined,
{ enabled: isAuthenticated }
)
// Initialize form with user data
useEffect(() => {
@@ -95,11 +104,17 @@ export default function OnboardingPage() {
}
}, [userData, initialized])
// Fetch feature flags
const { data: featureFlags } = trpc.settings.getFeatureFlags.useQuery()
// Fetch feature flags only after session is hydrated
const { data: featureFlags } = trpc.settings.getFeatureFlags.useQuery(
undefined,
{ enabled: isAuthenticated }
)
const whatsappEnabled = featureFlags?.whatsappEnabled ?? false
const completeOnboarding = trpc.user.completeOnboarding.useMutation()
const utils = trpc.useUtils()
const completeOnboarding = trpc.user.completeOnboarding.useMutation({
onSuccess: () => utils.user.me.invalidate(),
})
// Dynamic steps based on WhatsApp availability
const steps: Step[] = useMemo(() => {
@@ -162,8 +177,8 @@ export default function OnboardingPage() {
}
}
// Show loading while fetching user data
if (userLoading || !initialized) {
// Show loading while session hydrates or fetching user data
if (sessionStatus === 'loading' || userLoading || !initialized) {
return (
<div className="absolute inset-0 -m-4 flex items-center justify-center p-4 md:p-8 bg-gradient-to-br from-[#053d57] to-[#557f8c]">
<Card className="w-full max-w-lg shadow-2xl">