summaryrefslogblamecommitdiff
path: root/makima/frontend/src/components/contracts/ContractList.tsx
blob: 3a7b1633a812f64c3723a4a3b5f5aea594118207 (plain) (tree)















































































































































































                                                                                                                                                        
import { useState } from "react";
import type { ContractSummary, ContractStatus } from "../../lib/api";
import { PhaseBadge } from "./PhaseBadge";
import { PhaseProgressBarCompact } from "./PhaseProgressBar";

interface ContractListProps {
  contracts: ContractSummary[];
  loading: boolean;
  onSelect: (id: string) => void;
  onCreate: () => void;
  selectedId?: string;
}

const statusColors: Record<ContractStatus, string> = {
  active: "text-green-400",
  completed: "text-blue-400",
  archived: "text-[#555]",
};

export function ContractList({
  contracts,
  loading,
  onSelect,
  onCreate,
  selectedId,
}: ContractListProps) {
  const [filter, setFilter] = useState<ContractStatus | "all">("all");

  const filteredContracts =
    filter === "all"
      ? contracts
      : contracts.filter((c) => c.status === filter);

  if (loading) {
    return (
      <div className="panel h-full flex items-center justify-center">
        <div className="font-mono text-[#9bc3ff] text-sm">Loading...</div>
      </div>
    );
  }

  return (
    <div className="panel h-full flex flex-col">
      {/* Header */}
      <div className="p-4 border-b border-dashed border-[rgba(117,170,252,0.35)]">
        <div className="flex items-center justify-between mb-3">
          <h2 className="font-mono text-sm text-[#75aafc] uppercase tracking-wider">
            Contracts
          </h2>
          <button
            onClick={onCreate}
            className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
          >
            + New
          </button>
        </div>

        {/* Filter tabs */}
        <div className="flex gap-1">
          {(["all", "active", "completed", "archived"] as const).map((status) => (
            <button
              key={status}
              onClick={() => setFilter(status)}
              className={`
                px-2 py-1 font-mono text-[10px] uppercase tracking-wider transition-colors
                ${
                  filter === status
                    ? "bg-[rgba(117,170,252,0.1)] text-[#9bc3ff] border border-[rgba(117,170,252,0.3)]"
                    : "text-[#555] hover:text-[#75aafc]"
                }
              `}
            >
              {status}
            </button>
          ))}
        </div>
      </div>

      {/* Contract list */}
      <div className="flex-1 overflow-y-auto">
        {filteredContracts.length === 0 ? (
          <div className="p-4 text-center">
            <p className="font-mono text-sm text-[#555]">
              {filter === "all"
                ? "No contracts yet"
                : `No ${filter} contracts`}
            </p>
          </div>
        ) : (
          <div className="divide-y divide-[rgba(117,170,252,0.15)]">
            {filteredContracts.map((contract) => (
              <button
                key={contract.id}
                onClick={() => onSelect(contract.id)}
                className={`
                  w-full text-left p-4 transition-colors
                  ${
                    selectedId === contract.id
                      ? "bg-[rgba(117,170,252,0.1)]"
                      : "hover:bg-[rgba(117,170,252,0.05)]"
                  }
                `}
              >
                <div className="flex items-start justify-between gap-2 mb-2">
                  <h3 className="font-mono text-sm text-[#dbe7ff] truncate">
                    {contract.name}
                  </h3>
                  <span
                    className={`text-[10px] font-mono uppercase ${
                      statusColors[contract.status]
                    }`}
                  >
                    {contract.status}
                  </span>
                </div>

                {contract.description && (
                  <p className="font-mono text-xs text-[#555] mb-2 line-clamp-2">
                    {contract.description}
                  </p>
                )}

                <div className="flex items-center justify-between">
                  <PhaseProgressBarCompact currentPhase={contract.phase} />
                  <div className="flex items-center gap-3 text-[10px] font-mono text-[#555]">
                    {contract.fileCount > 0 && (
                      <span>{contract.fileCount} files</span>
                    )}
                    {contract.taskCount > 0 && (
                      <span>{contract.taskCount} tasks</span>
                    )}
                    {contract.repositoryCount > 0 && (
                      <span>{contract.repositoryCount} repos</span>
                    )}
                  </div>
                </div>
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

export function ContractCard({
  contract,
  onClick,
}: {
  contract: ContractSummary;
  onClick: () => void;
}) {
  return (
    <button
      onClick={onClick}
      className="w-full text-left p-4 border border-[rgba(117,170,252,0.2)] hover:border-[rgba(117,170,252,0.4)] transition-colors"
    >
      <div className="flex items-start justify-between gap-2 mb-2">
        <h3 className="font-mono text-sm text-[#dbe7ff]">{contract.name}</h3>
        <PhaseBadge phase={contract.phase} />
      </div>

      {contract.description && (
        <p className="font-mono text-xs text-[#555] mb-3 line-clamp-2">
          {contract.description}
        </p>
      )}

      <div className="flex items-center gap-4 text-[10px] font-mono text-[#555]">
        <span>{contract.fileCount} files</span>
        <span>{contract.taskCount} tasks</span>
        <span>{contract.repositoryCount} repos</span>
      </div>
    </button>
  );
}