File size: 5,788 Bytes
0b85fad 830bf88 0b85fad e92d5c0 0b85fad e92d5c0 51e559a 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad 51e559a e92d5c0 51e559a 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad e92d5c0 0b85fad 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a e92d5c0 51e559a 0b85fad e92d5c0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
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>
);
}
|