Wauplin's picture
Wauplin HF Staff
final?
830bf88 verified
import { useState, useEffect } from "react";
import { BACKEND_URL, APP_CONFIG } from "../constants";
interface BackendHealthCheckProps {
checkInterval?: number;
}
export function BackendHealthCheck({
checkInterval = APP_CONFIG.HEALTH_CHECK_INTERVAL,
}: BackendHealthCheckProps) {
const [backendStatus, setBackendStatus] = useState<
"loading" | "online" | "offline"
>("loading");
const [showTooltip, setShowTooltip] = useState(false);
const [copied, setCopied] = useState(false);
const healthCheckUrl = `${BACKEND_URL}/api/health`;
useEffect(() => {
const checkBackendHealth = async () => {
try {
const response = await fetch(healthCheckUrl);
if (response.ok) {
const data = await response.json();
if (data.status === "ok") {
setBackendStatus("online");
} else {
setBackendStatus("offline");
}
} else {
setBackendStatus("offline");
}
} catch {
setBackendStatus("offline");
}
};
checkBackendHealth();
// Check health at specified interval
const interval = setInterval(checkBackendHealth, checkInterval);
return () => clearInterval(interval);
}, [healthCheckUrl, checkInterval]);
const handleCopyCode = async () => {
const codeText = `cd backend/\nmake install\nmake backend`;
try {
await navigator.clipboard.writeText(codeText);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error("Failed to copy text: ", err);
}
};
const getStatusColor = () => {
switch (backendStatus) {
case "online":
return "bg-green-500";
case "offline":
return "bg-red-500";
case "loading":
return "bg-yellow-500";
default:
return "bg-gray-500";
}
};
const getStatusText = () => {
switch (backendStatus) {
case "online":
return "Backend Online";
case "offline":
return "Backend Offline";
case "loading":
return "Checking Backend...";
default:
return "Unknown Status";
}
};
return (
<div className="absolute top-4 right-4">
<div
className="relative flex items-center space-x-2 bg-gray-800 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-700 transition-colors"
onMouseEnter={() => backendStatus === "offline" && setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
<div
className={`w-3 h-3 rounded-full ${getStatusColor()} animate-pulse`}
></div>
<span className="text-sm font-medium">{getStatusText()}</span>
{/* Info icon when backend is offline */}
{backendStatus === "offline" && (
<svg
className="w-5 h-5 text-red-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
)}
{/* Tooltip for offline backend */}
{showTooltip && backendStatus === "offline" && (
<div
className="absolute top-full right-0 mt-2 w-fit bg-gray-900 text-white rounded-lg p-4 shadow-xl border border-gray-700 z-50"
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
<div className="flex items-start space-x-3">
<div className="flex-1 min-w-0">
<h3 className="text-sm font-semibold text-400 mb-3 text-left">
Start the server with:
</h3>
<div className="relative">
<code className="text-xs bg-gray-800 text-green-400 px-3 py-2 rounded font-mono block w-fit overflow-x-auto text-left pr-12">
cd backend/
<br />
make install
<br />
make backend
</code>
<button
onClick={handleCopyCode}
className="absolute top-1 right-1 p-1 bg-gray-700 hover:bg-gray-600 rounded text-gray-300 hover:text-white transition-colors"
title="Copy to clipboard"
>
{copied ? (
<svg
className="w-4 h-4 text-green-400"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
) : (
<svg
className="w-4 h-4"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
)}
</button>
</div>
</div>
</div>
{/* Arrow pointing up */}
<div className="absolute bottom-full right-4 w-0 h-0 border-l-4 border-r-4 border-b-4 border-transparent border-b-gray-900"></div>
</div>
)}
</div>
</div>
);
}