All files / src/web/components TimeFilter.tsx

100% Statements 59/59
92.3% Branches 12/13
100% Functions 5/5
100% Lines 59/59

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 811x 1x                   1x 20x 20x 20x 20x 20x 20x   20x 20x 20x 20x 20x 20x 20x 20x   20x     20x 8x 1x 1x 1x 1x   8x 8x 8x 8x 20x   20x 4x 4x 4x   20x 20x 20x 20x 20x 20x 20x   20x 20x 20x   20x 7x 7x 42x 42x 42x 42x 7x 35x 42x   42x 42x 7x 7x   20x   20x  
import { Clock } from 'lucide-react';
import { FC, useEffect, useRef, useState } from 'react';
 
export type TimeRange = 'day' | 'last24h' | '2days' | 'week' | 'month' | 'all';
 
interface TimeFilterProps {
  selectedRange: TimeRange;
  onChange: (range: TimeRange) => void;
  className?: string;
}
 
export const TimeFilter: FC<TimeFilterProps> = ({
  selectedRange,
  onChange,
  className = ''
}) => {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef<HTMLDivElement>(null);
 
  const options: { value: TimeRange; label: string }[] = [
    { value: 'day', label: 'Today' },
    { value: 'last24h', label: 'Last day' },
    { value: '2days', label: 'Last 2 days' },
    { value: 'week', label: 'This week' },
    { value: 'month', label: 'This month' },
    { value: 'all', label: 'All time' },
  ];
 
  const selectedLabel = options.find(opt => opt.value === selectedRange)?.label || 'All time';
 
  // Close dropdown when clicking outside
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
        setIsOpen(false);
      }
    };
 
    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, []);
 
  const handleSelect = (range: TimeRange) => {
    onChange(range);
    setIsOpen(false);
  };
 
  return (
    <div className={`relative ${className}`} ref={dropdownRef}>
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="flex items-center gap-1 px-2 py-1.5 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-800 rounded-full transition-colors duration-200"
        aria-haspopup="true"
        aria-expanded={isOpen}
      >
        <Clock className="h-4 w-4" />
        <span className="ml-1">{selectedLabel}</span>
      </button>
 
      {isOpen && (
        <div className="absolute left-0 mt-1 w-40 bg-white rounded-md shadow-lg py-1 z-10 border border-gray-200">
          {options.map((option) => (
            <button
              key={option.value}
              onClick={() => handleSelect(option.value)}
              className={`w-full text-left px-4 py-2 text-sm ${selectedRange === option.value
                ? 'bg-primary-50 text-primary-700'
                : 'text-gray-700 hover:bg-gray-100'
                }`}
            >
              {option.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};