feat: Footer - live health-check polling Brain API every 60s with status indicator
#25
by MouleeswaranM - opened
landing/src/components/Footer.tsx
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
|
|
| 1 |
import './Footer.css'
|
| 2 |
|
|
|
|
|
|
|
| 3 |
export default function Footer() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
return (
|
| 5 |
<footer className="footer">
|
| 6 |
<div className="container footer__inner">
|
|
@@ -20,8 +54,8 @@ export default function Footer() {
|
|
| 20 |
</div>
|
| 21 |
<p>Fair routes. Happy drivers. Explainable by default.</p>
|
| 22 |
<div className="footer__status">
|
| 23 |
-
<span className="footer__status-dot" />
|
| 24 |
-
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
|
|
@@ -30,32 +64,36 @@ export default function Footer() {
|
|
| 30 |
<div className="footer__col-title">Product</div>
|
| 31 |
<a href="#features">Features</a>
|
| 32 |
<a href="#pricing">Pricing</a>
|
| 33 |
-
<a href="
|
| 34 |
<a href="#demo">Live Demo</a>
|
| 35 |
</div>
|
| 36 |
<div className="footer__col">
|
| 37 |
<div className="footer__col-title">Developers</div>
|
| 38 |
-
<a href="
|
| 39 |
-
<a href="
|
| 40 |
-
<a href="
|
| 41 |
-
<a href="
|
| 42 |
</div>
|
| 43 |
<div className="footer__col">
|
| 44 |
<div className="footer__col-title">Company</div>
|
| 45 |
-
<a href="
|
| 46 |
-
<a href="
|
| 47 |
-
<a href="
|
| 48 |
-
<a href="#">
|
| 49 |
</div>
|
| 50 |
</div>
|
| 51 |
</div>
|
| 52 |
|
| 53 |
<div className="footer__bottom">
|
| 54 |
<div className="container">
|
| 55 |
-
<span>© 2026 FairRelay. Built for fair logistics.</span>
|
| 56 |
-
<span>Made with 💜 for
|
| 57 |
</div>
|
| 58 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
</footer>
|
| 60 |
)
|
| 61 |
}
|
|
|
|
| 1 |
+
import { useState, useEffect } from 'react'
|
| 2 |
import './Footer.css'
|
| 3 |
|
| 4 |
+
const API_URL = import.meta.env.VITE_API_URL || 'https://fairrelay-backend.onrender.com'
|
| 5 |
+
|
| 6 |
export default function Footer() {
|
| 7 |
+
const [status, setStatus] = useState<'checking' | 'operational' | 'degraded' | 'down'>('checking')
|
| 8 |
+
|
| 9 |
+
useEffect(() => {
|
| 10 |
+
const checkHealth = async () => {
|
| 11 |
+
try {
|
| 12 |
+
const res = await fetch(`${API_URL}/health`, { signal: AbortSignal.timeout(5000) })
|
| 13 |
+
if (res.ok) {
|
| 14 |
+
const data = await res.json()
|
| 15 |
+
setStatus(data.status === 'healthy' ? 'operational' : 'degraded')
|
| 16 |
+
} else {
|
| 17 |
+
setStatus('degraded')
|
| 18 |
+
}
|
| 19 |
+
} catch {
|
| 20 |
+
setStatus('down')
|
| 21 |
+
}
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
checkHealth()
|
| 25 |
+
const interval = setInterval(checkHealth, 60000) // Poll every 60s
|
| 26 |
+
return () => clearInterval(interval)
|
| 27 |
+
}, [])
|
| 28 |
+
|
| 29 |
+
const statusConfig = {
|
| 30 |
+
checking: { color: '#3b82f6', text: 'Checking status…', pulse: true },
|
| 31 |
+
operational: { color: '#10b981', text: 'All systems operational', pulse: false },
|
| 32 |
+
degraded: { color: '#f59e0b', text: 'Degraded performance', pulse: true },
|
| 33 |
+
down: { color: '#ef4444', text: 'Service warming up', pulse: true },
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
const s = statusConfig[status]
|
| 37 |
+
|
| 38 |
return (
|
| 39 |
<footer className="footer">
|
| 40 |
<div className="container footer__inner">
|
|
|
|
| 54 |
</div>
|
| 55 |
<p>Fair routes. Happy drivers. Explainable by default.</p>
|
| 56 |
<div className="footer__status">
|
| 57 |
+
<span className="footer__status-dot" style={{ background: s.color, boxShadow: s.pulse ? `0 0 8px ${s.color}` : 'none', animation: s.pulse ? 'pulse 2s infinite' : 'none' }} />
|
| 58 |
+
{s.text}
|
| 59 |
</div>
|
| 60 |
</div>
|
| 61 |
|
|
|
|
| 64 |
<div className="footer__col-title">Product</div>
|
| 65 |
<a href="#features">Features</a>
|
| 66 |
<a href="#pricing">Pricing</a>
|
| 67 |
+
<a href={`${API_URL}/docs`} target="_blank" rel="noopener">API Reference ↗</a>
|
| 68 |
<a href="#demo">Live Demo</a>
|
| 69 |
</div>
|
| 70 |
<div className="footer__col">
|
| 71 |
<div className="footer__col-title">Developers</div>
|
| 72 |
+
<a href={`${API_URL}/docs`} target="_blank" rel="noopener">Getting Started ↗</a>
|
| 73 |
+
<a href={`${API_URL}/redoc`} target="_blank" rel="noopener">Endpoint Reference ↗</a>
|
| 74 |
+
<a href="https://github.com/MUTHUKUMARAN-K-1/FairRelay" target="_blank" rel="noopener">GitHub ↗</a>
|
| 75 |
+
<a href={`${API_URL}/health`} target="_blank" rel="noopener">Status Page ↗</a>
|
| 76 |
</div>
|
| 77 |
<div className="footer__col">
|
| 78 |
<div className="footer__col-title">Company</div>
|
| 79 |
+
<a href="https://logisticsnow.in" target="_blank" rel="noopener">LogisticsNow ↗</a>
|
| 80 |
+
<a href="https://company.lorri.in" target="_blank" rel="noopener">LoRRI Platform ↗</a>
|
| 81 |
+
<a href="mailto:muthukumaran@logisticsnow.in">Contact</a>
|
| 82 |
+
<a href="#">Privacy Policy</a>
|
| 83 |
</div>
|
| 84 |
</div>
|
| 85 |
</div>
|
| 86 |
|
| 87 |
<div className="footer__bottom">
|
| 88 |
<div className="container">
|
| 89 |
+
<span>© 2026 FairRelay by LogisticsNow Pvt. Ltd. Built for fair logistics.</span>
|
| 90 |
+
<span>Integrated with <a href="https://logisticsnow.in" target="_blank" rel="noopener" style={{ color: '#f97316' }}>LoRRI</a> · Made with 💜 for 15M+ drivers</span>
|
| 91 |
</div>
|
| 92 |
</div>
|
| 93 |
+
|
| 94 |
+
<style>{`
|
| 95 |
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
| 96 |
+
`}</style>
|
| 97 |
</footer>
|
| 98 |
)
|
| 99 |
}
|