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>
  );
}