All files / src/pages/flowsheet-page/flowsheet/PropertiesSidebar MonitoringTableList.tsx

84.05% Statements 58/69
63.41% Branches 52/82
54.54% Functions 6/11
87.03% Lines 47/54

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 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                                                98x             17x     52x           5x   46x 46x 54x     1x 1x   184x 92x                   60x         60x 60x   60x       222x   60x                     222x       60x 60x     9x               78x     60x 8x   68x 54x   60x   1x 1x 114x   60x   9x       69x 276x 168x 54x   7x                   60x   8x 60x         68x 54x 60x   1x         168x     168x 438x      
import { Eye, EyeOff, Table2, Trash2 } from "lucide-react";
import type { KeyboardEvent } from "react";
import { AccessControlledButton as Button } from "@/ahuora-design-system/ui/accessControlled";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/ahuora-design-system/ui/alert-dialog";
import { MonitoringTableRead } from "@/api/apiStore.gen";
import { useFlowsheetAccess } from "@/hooks/flowsheetAccess";
import { cn } from "@/lib/utils";
 
interface MonitoringTableListProps {
  tables: MonitoringTableRead[];
  onToggleVisibility: (tableId: number) => void;
  onDeleteTable: (tableId: number) => void;
}
 
export function MonitoringTableList({
  tables,
  onToggleVisibility,
  onDeleteTable,
}: MonitoringTableListProps) {
  if (tables.length === 0) {
    return (
      <div className="text-sm text-muted-foreground py-2">
        No monitoring tables created yet. Click "Create New Monitoring Table"
        above to add one.
      </div>
    );
  }
 
  return (
    <div className="flex flex-col gap-2">
      <p className="text-sm text-muted-foreground font-medium tracking-wide">
        Monitoring Tables
      </p>
      {tables.map((table) => (
        <MonitoringTableListItem
          key={table.id}
          table={table}
          onToggleVisibility={() => onToggleVisibility(table.id)}
          onDelete={() => onDeleteTable(table.id)}
        />
      ))}
    </div>
  );
}
 
interface MonitoringTableListItemProps {
  table: MonitoringTableRead;
  onToggleVisibility: () => void;
  onDelete: () => void;
}
 
function MonitoringTableListItem({
  table,
  onToggleVisibility,
  onDelete,
}: MonitoringTableListItemProps) {
  const access = useFlowsheetAccess();
  const canMutate = access?.can_edit ?? false;
 
  const showHiddenTable = () => {
    if (canMutate && !table.visible) {
      onToggleVisibility();
    }
  };
 
  const handleRowKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
    if (
      !canMutate ||
      table.visible ||
      (event.key !== "Enter" && event.key !== " ")
    ) {
      return;
    }
 
    event.preventDefault();
    onToggleVisibility();
  };
 
  return (
    <div
      role={canMutate && !table.visible ? "button" : undefined}
      tabIndex={canMutate && !table.visible ? 0 : undefined}
      onClick={showHiddenTable}
      onKeyDown={handleRowKeyDown}
      className={cn(
        "flex items-center justify-between gap-2 p-2 rounded-md border",
        table.visible
          ? "bg-accent/50 border-accent"
          : cn(
              "bg-muted/30 border-border",
              canMutate && "cursor-pointer hover:bg-muted/50",
            ),
      )}
    >
      <div className="flex items-center gap-2 min-w-0 flex-1">
        <Table2 className="h-4 w-4 shrink-0 text-muted-foreground" />
        <span className="text-sm truncate" title={table.title}>
          {table.title}
        </span>
        <button
          type="button"
          disabled={!canMutate}
          onClick={(event) => {
            event.stopPropagation();
            onToggleVisibility();
          }}
          className="p-0.5 rounded hover:bg-muted/50 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
          title={table.visible ? "Hide table" : "Show table"}
        >
          {table.visible ? (
            <Eye className="h-3.5 w-3.5 shrink-0 text-green-600 cursor-pointer" />
          ) : (
            <EyeOff className="h-3.5 w-3.5 shrink-0 text-muted-foreground cursor-pointer" />
          )}
        </button>
      </div>
      <div className="flex items-center gap-1 shrink-0">
        <AlertDialog>
          <AlertDialogTrigger asChild>
            <Button
              variant="ghost"
              size="sm"
              className="h-7 px-2 text-destructive hover:text-destructive hover:bg-destructive/10"
              title="Delete table permanently"
              onClick={(event) => event.stopPropagation()}
            >
              <Trash2 className="h-3.5 w-3.5" />
            </Button>
          </AlertDialogTrigger>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogTitle>Delete Monitoring Table</AlertDialogTitle>
              <AlertDialogDescription>
                Are you sure you want to permanently delete "{table.title}"?
                This action cannot be undone.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <AlertDialogCancel>Cancel</AlertDialogCancel>
              <AlertDialogAction
                onClick={() => canMutate && onDelete()}
                className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
              >
                Delete
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>
      </div>
    </div>
  );
}