Files
MOPC-Portal/src/components/shared/version-guard.tsx
Matt 2e8ab91e07
All checks were successful
Build and Push Docker Image / build (push) Successful in 11m12s
fix: version guard uses static file, members table shows project name with round badge
Version guard:
- Replace API route with prebuild-generated public/build-id.json
- Captures build ID on first load, only notifies on mismatch
- Fixes false positive refresh prompts from env mismatch

Members table (applicants):
- Show project name + round badge instead of round name + state
- Red badge for rejected, gray for withdrawn, green for passed,
  outline for active rounds
- Include projectName in applicantRoundInfo from backend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:23:24 +01:00

62 lines
1.7 KiB
TypeScript

'use client'
import { useEffect, useRef } from 'react'
import { toast } from 'sonner'
// Capture the build ID when this module first loads (from the current deployment's JS bundle).
// On subsequent fetches of /build-id.json, if the value differs, a new deploy happened.
let initialBuildId: string | null = null
export function VersionGuard() {
const notified = useRef(false)
useEffect(() => {
async function checkVersion() {
if (notified.current) return
try {
const res = await fetch('/build-id.json?t=' + Date.now())
if (!res.ok) return
const { buildId } = await res.json()
if (!buildId) return
// First load — capture the build ID
if (initialBuildId === null) {
initialBuildId = buildId
return
}
// Subsequent checks — compare
if (buildId !== initialBuildId) {
notified.current = true
toast('A new version is available', {
description: 'Refresh to get the latest updates.',
duration: Infinity,
action: {
label: 'Refresh',
onClick: () => window.location.reload(),
},
})
}
} catch {
// Network error — ignore
}
}
// Initial check (captures build ID)
checkVersion()
// Check on tab focus (covers users returning to stale tabs)
window.addEventListener('focus', checkVersion)
// Also check every 5 minutes for long-lived tabs
const interval = setInterval(checkVersion, 5 * 60 * 1000)
return () => {
window.removeEventListener('focus', checkVersion)
clearInterval(interval)
}
}, [])
return null
}