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:
@@ -141,28 +141,39 @@ export default function AwardDetailPage({
|
||||
const { id: awardId } = use(params)
|
||||
const router = useRouter()
|
||||
|
||||
// State declarations (before queries that depend on them)
|
||||
const [isPollingJob, setIsPollingJob] = useState(false)
|
||||
const pollingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
const [selectedJurorId, setSelectedJurorId] = useState('')
|
||||
const [includeSubmitted, setIncludeSubmitted] = useState(true)
|
||||
const [addProjectDialogOpen, setAddProjectDialogOpen] = useState(false)
|
||||
const [projectSearchQuery, setProjectSearchQuery] = useState('')
|
||||
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
|
||||
const [activeTab, setActiveTab] = useState('eligibility')
|
||||
|
||||
// Core queries
|
||||
const { data: award, isLoading, refetch } =
|
||||
trpc.specialAward.get.useQuery({ id: awardId })
|
||||
const { data: eligibilityData, refetch: refetchEligibility } =
|
||||
trpc.specialAward.listEligible.useQuery({
|
||||
awardId,
|
||||
page: 1,
|
||||
perPage: 500,
|
||||
perPage: 100,
|
||||
})
|
||||
const { data: jurors, refetch: refetchJurors } =
|
||||
trpc.specialAward.listJurors.useQuery({ awardId })
|
||||
const { data: voteResults } =
|
||||
trpc.specialAward.getVoteResults.useQuery({ awardId })
|
||||
const { data: allUsers } = trpc.user.list.useQuery({ role: 'JURY_MEMBER', page: 1, perPage: 100 })
|
||||
|
||||
// Fetch all projects in the program for manual eligibility addition
|
||||
const { data: allProjects } = trpc.project.list.useQuery(
|
||||
{ programId: award?.programId ?? '', perPage: 500 },
|
||||
{ enabled: !!award?.programId }
|
||||
// Deferred queries - only load when needed
|
||||
const { data: allUsers } = trpc.user.list.useQuery(
|
||||
{ role: 'JURY_MEMBER', page: 1, perPage: 100 },
|
||||
{ enabled: activeTab === 'jurors' }
|
||||
)
|
||||
const { data: allProjects } = trpc.project.list.useQuery(
|
||||
{ programId: award?.programId ?? '', perPage: 200 },
|
||||
{ enabled: !!award?.programId && addProjectDialogOpen }
|
||||
)
|
||||
|
||||
const [isPollingJob, setIsPollingJob] = useState(false)
|
||||
const pollingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
// Eligibility job polling
|
||||
const { data: jobStatus, refetch: refetchJobStatus } =
|
||||
@@ -208,19 +219,34 @@ export default function AwardDetailPage({
|
||||
}
|
||||
}, [award?.eligibilityJobStatus])
|
||||
|
||||
const updateStatus = trpc.specialAward.updateStatus.useMutation()
|
||||
const runEligibility = trpc.specialAward.runEligibility.useMutation()
|
||||
const setEligibility = trpc.specialAward.setEligibility.useMutation()
|
||||
const addJuror = trpc.specialAward.addJuror.useMutation()
|
||||
const removeJuror = trpc.specialAward.removeJuror.useMutation()
|
||||
const setWinner = trpc.specialAward.setWinner.useMutation()
|
||||
const deleteAward = trpc.specialAward.delete.useMutation()
|
||||
|
||||
const [selectedJurorId, setSelectedJurorId] = useState('')
|
||||
const [includeSubmitted, setIncludeSubmitted] = useState(true)
|
||||
const [addProjectDialogOpen, setAddProjectDialogOpen] = useState(false)
|
||||
const [projectSearchQuery, setProjectSearchQuery] = useState('')
|
||||
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
|
||||
const utils = trpc.useUtils()
|
||||
const invalidateAward = () => {
|
||||
utils.specialAward.get.invalidate({ id: awardId })
|
||||
utils.specialAward.listEligible.invalidate({ awardId })
|
||||
utils.specialAward.listJurors.invalidate({ awardId })
|
||||
utils.specialAward.getVoteResults.invalidate({ awardId })
|
||||
}
|
||||
const updateStatus = trpc.specialAward.updateStatus.useMutation({
|
||||
onSuccess: invalidateAward,
|
||||
})
|
||||
const runEligibility = trpc.specialAward.runEligibility.useMutation({
|
||||
onSuccess: invalidateAward,
|
||||
})
|
||||
const setEligibility = trpc.specialAward.setEligibility.useMutation({
|
||||
onSuccess: () => utils.specialAward.listEligible.invalidate({ awardId }),
|
||||
})
|
||||
const addJuror = trpc.specialAward.addJuror.useMutation({
|
||||
onSuccess: () => utils.specialAward.listJurors.invalidate({ awardId }),
|
||||
})
|
||||
const removeJuror = trpc.specialAward.removeJuror.useMutation({
|
||||
onSuccess: () => utils.specialAward.listJurors.invalidate({ awardId }),
|
||||
})
|
||||
const setWinner = trpc.specialAward.setWinner.useMutation({
|
||||
onSuccess: invalidateAward,
|
||||
})
|
||||
const deleteAward = trpc.specialAward.delete.useMutation({
|
||||
onSuccess: () => utils.specialAward.list.invalidate(),
|
||||
})
|
||||
|
||||
const handleStatusChange = async (
|
||||
status: 'DRAFT' | 'NOMINATIONS_OPEN' | 'VOTING_OPEN' | 'CLOSED' | 'ARCHIVED'
|
||||
@@ -569,7 +595,7 @@ export default function AwardDetailPage({
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<Tabs defaultValue="eligibility">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="eligibility">
|
||||
<CheckCircle2 className="mr-2 h-4 w-4" />
|
||||
|
||||
Reference in New Issue
Block a user