File size: 4,611 Bytes
5feba25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useState, useRef, useEffect } from 'react'
import { createPortal } from 'react-dom'
import { CheckCircle, Circle, Info, X } from 'lucide-react'

/**
 * MetricItem Component
 *
 * Handles individual health metric with isolated hover state
 * Uses React Portal to render popover outside the scrollable sidebar
 * Ensures popover floats over the chat area without being clipped
 */
const MetricItem = ({ feature, displayLabel, definition, isCollected }) => {
  const [showHint, setShowHint] = useState(false)
  const [popoverPosition, setPopoverPosition] = useState({ top: 0, left: 0 })
  const iconRef = useRef(null)

  // Calculate popover position based on icon location
  useEffect(() => {
    if (!showHint || !iconRef.current) return

    const iconRect = iconRef.current.getBoundingClientRect()

    // Position popover to the right of the icon, aligned with top
    // Add some spacing (16px) to the right
    const left = iconRect.right + 16
    const top = iconRect.top - 8  // Slight vertical centering

    setPopoverPosition({ top, left })
  }, [showHint])

  // Handle mouse leaving the metric item
  const handleMouseLeave = () => {
    setShowHint(false)
  }

  // Handle mouse entering the metric item
  const handleMouseEnter = () => {
    setShowHint(true)
  }

  // Close hint when clicking X button
  const handleCloseHint = (e) => {
    e.stopPropagation()
    setShowHint(false)
  }

  return (
    <>
      {/* Metric Item Row */}
      <div
        className={`flex items-center justify-between gap-2 text-xs p-2 rounded transition-all duration-150 ${
          isCollected
            ? 'text-green-700 bg-green-50 hover:bg-green-100'
            : 'text-gray-400 hover:bg-gray-100'
        }`}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      >
        {/* Status icon + Label */}
        <div className="flex items-center gap-2 flex-1 min-w-0">
          {isCollected ? (
            <CheckCircle size={16} className="flex-shrink-0" />
          ) : (
            <Circle size={16} className="flex-shrink-0" />
          )}
          <span className="truncate">{displayLabel}</span>
        </div>

        {/* Info Icon Button */}
        <button
          ref={iconRef}
          onClick={() => setShowHint(!showHint)}
          className="p-1 rounded-full text-gray-400 hover:text-gray-600 hover:bg-gray-200 transition-all duration-150 cursor-help flex-shrink-0"
          title="Click for definition"
          aria-label={`Information about ${displayLabel}`}
        >
          <Info size={14} />
        </button>
      </div>

      {/* Popover - Rendered via Portal (outside sidebar) with Glassmorphism */}
      {showHint &&
        createPortal(
          <div
            className="fixed z-9999 w-80 rounded-2xl shadow-lg p-4 text-xs leading-relaxed animate-in fade-in duration-200 pointer-events-auto backdrop-blur-md"
            style={{
              top: `${popoverPosition.top}px`,
              left: `${popoverPosition.left}px`,
              backgroundColor: 'rgba(220, 252, 231, 0.75)',
              borderColor: 'rgba(134, 239, 172, 0.5)',
              borderWidth: '1px',
              WebkitBackdropFilter: 'blur(12px)',
            }}
            onMouseEnter={() => setShowHint(true)}
            onMouseLeave={() => setShowHint(false)}
          >
            {/* Header with feature name and close button */}
            <div className="flex items-start justify-between gap-3 mb-3">
              <span className="font-semibold text-green-900 text-sm">{displayLabel}</span>
              <button
                onClick={handleCloseHint}
                className="flex-shrink-0 text-green-700 hover:text-green-900 transition-colors p-0.5 hover:bg-green-200/40 rounded-lg"
                aria-label="Close popover"
              >
                <X size={16} />
              </button>
            </div>

            {/* Definition text */}
            <p className="text-green-900 leading-relaxed mb-2 font-medium">{definition}</p>

            {/* Arrow pointer - points back to info icon (matches popover styling) */}
            <div
              className="absolute w-3 h-3 transform rotate-45"
              style={{
                right: '-6px',
                top: `${8}px`,
                backgroundColor: 'rgba(220, 252, 231, 0.85)',
                borderTop: '1px solid rgba(134, 239, 172, 0.4)',
                borderLeft: '1px solid rgba(134, 239, 172, 0.4)',
              }}
            />
          </div>,
          document.body
        )}
    </>
  )
}

export default MetricItem