All files / src/components/monitors MonitorList.tsx

100% Statements 90/90
100% Branches 17/17
100% Functions 6/6
100% Lines 90/90

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 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 1111x 1x 1x   1x                 47x 47x 47x 47x 47x 47x 47x 47x   47x 3x 2x 2x 2x 3x   47x 5x 5x 5x   5x 5x   5x   42x 42x 42x 42x 42x 42x 42x 42x 42x 42x   42x 42x 42x 42x 42x 89x 89x 89x 89x 89x 89x 89x 89x 89x 89x   89x 89x 89x 89x 89x 89x 89x 89x 89x 89x 89x 89x   89x 89x 89x 89x 89x 89x 89x 89x 89x 89x 89x 89x 89x   89x 89x 89x 89x 89x   89x 89x 89x 89x 89x 42x 42x 42x 42x   42x  
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import StatusIndicator from "./StatusIndicator";
import type { MonitorRead } from "../../types/monitor";
import { formatInterval } from "../../utils/formatters";
 
interface MonitorListProps {
  monitors: MonitorRead[];
  onDelete: (id: string) => void;
  onToggle: (id: string, isActive: boolean) => void;
  onEdit: (monitor: MonitorRead) => void;
}
 
export default function MonitorList({
  monitors,
  onDelete,
  onToggle,
  onEdit,
}: MonitorListProps) {
  const [deletingId, setDeletingId] = useState<string | null>(null);
  const navigate = useNavigate();
 
  const handleDelete = async (id: string) => {
    if (!confirm("Delete this monitor?")) return;
    setDeletingId(id);
    await onDelete(id);
    setDeletingId(null);
  };
 
  if (monitors.length === 0) {
    return (
      <div className="rounded-lg border border-dashed border-gray-300 p-8 text-center">
        <p className="text-gray-500">
          No monitors yet. Create one to get started.
        </p>
      </div>
    );
  }
 
  return (
    <div className="overflow-hidden rounded-lg border border-gray-200">
      <table className="w-full text-left text-sm">
        <thead className="bg-gray-50">
          <tr>
            <th className="px-4 py-3 font-medium text-gray-700">Status</th>
            <th className="px-4 py-3 font-medium text-gray-700">URL</th>
            <th className="px-4 py-3 font-medium text-gray-700">Interval</th>
            <th className="px-4 py-3 font-medium text-gray-700">Active</th>
            <th className="px-4 py-3 font-medium text-gray-700 text-right">
              Actions
            </th>
          </tr>
        </thead>
        <tbody className="divide-y divide-gray-200">
          {monitors.map((monitor) => (
            <tr key={monitor.id} className="hover:bg-gray-50">
              <td className="px-4 py-3">
                <StatusIndicator
                  status={monitor.alert_status as "UP" | "DOWN"}
                />
              </td>
              <td className="px-4 py-3">
                <button
                  onClick={() => navigate(`/monitor/${monitor.id}`)}
                  className="text-blue-600 hover:underline text-left truncate max-w-50 cursor-pointer"
                >
                  {monitor.url}
                </button>
              </td>
              <td className="px-4 py-3 text-gray-600">
                {formatInterval(monitor.interval_seconds)}
              </td>
              <td className="px-4 py-3">
                <button
                  onClick={() => onToggle(monitor.id, !monitor.is_active)}
                  className={`cursor-pointer relative inline-flex h-5 w-9 items-center rounded-full transition-colors ${
                    monitor.is_active ? "bg-blue-600" : "bg-gray-300"
                  }`}
                >
                  <span
                    className={`inline-block h-3.5 w-3.5 transform rounded-full bg-white transition-transform ${
                      monitor.is_active ? "translate-x-5" : "translate-x-1"
                    }`}
                  />
                </button>
              </td>
              <td className="px-4 py-3 text-right">
                <div className="flex items-center justify-end gap-3">
                  <button
                    onClick={() => onEdit(monitor)}
                    className="text-blue-600 hover:text-blue-800 text-sm font-medium cursor-pointer"
                  >
                    Edit
                  </button>
                  <button
                    onClick={() => handleDelete(monitor.id)}
                    disabled={deletingId === monitor.id}
                    className="text-red-600 hover:text-red-800 text-sm font-medium disabled:opacity-50 cursor-pointer"
                  >
                    {deletingId === monitor.id ? "Deleting..." : "Delete"}
                  </button>
                </div>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}