File size: 5,455 Bytes
0b85fad
 
 
 
 
 
 
 
 
 
 
51e559a
 
 
 
 
0b85fad
51e559a
 
0b85fad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51e559a
 
 
 
 
 
 
 
 
 
 
0b85fad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51e559a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b85fad
 
 
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
import { useState, useEffect } from "react";
import { BACKEND_URL, APP_CONFIG } from "../config/constants";

interface BackendHealthCheckProps {
  backendUrl?: string;
  checkInterval?: number;
}

export function BackendHealthCheck({ 
  checkInterval = APP_CONFIG.HEALTH_CHECK_INTERVAL
}: BackendHealthCheckProps) {
  // Only render in development mode
  if (import.meta.env.PROD) {
    return null;
  }

  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 (error) {
        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>
  );
}