summaryrefslogblamecommitdiff
path: root/makima/frontend/src/components/directives/DirectiveDetail.tsx
blob: 3634a790dfde3ba89f1ff4984aaf731b0b7a2a7b (plain) (tree)







































































































































































































                                                                                                                   
import type {
  DirectiveWithChains,
  DirectiveStatus,
  DirectiveChain,
} from "../../lib/api";

interface DirectiveDetailProps {
  directive: DirectiveWithChains;
  onBack: () => void;
  onDelete?: (id: string) => void;
}

const statusColors: Record<DirectiveStatus, string> = {
  draft: "text-[#888]",
  planning: "text-yellow-400",
  active: "text-green-400",
  paused: "text-orange-400",
  completed: "text-blue-400",
  archived: "text-[#555]",
  failed: "text-red-400",
};

function ChainCard({ chain }: { chain: DirectiveChain }) {
  return (
    <div className="p-3 border border-dashed border-[rgba(117,170,252,0.25)] bg-[rgba(117,170,252,0.03)]">
      <div className="flex items-center justify-between mb-1">
        <span className="font-mono text-xs text-[#dbe7ff]">{chain.name}</span>
        <span className="font-mono text-[10px] text-[#7788aa] uppercase">
          gen {chain.generation} &middot; {chain.status}
        </span>
      </div>
      {chain.description && (
        <p className="font-mono text-[11px] text-[#7788aa] mb-1">
          {chain.description}
        </p>
      )}
      <div className="flex gap-3 font-mono text-[10px] text-[#7788aa]">
        <span>
          {chain.completedSteps}/{chain.totalSteps} steps
        </span>
        {chain.failedSteps > 0 && (
          <span className="text-red-400">{chain.failedSteps} failed</span>
        )}
        {chain.currentConfidence != null && (
          <span>confidence: {(chain.currentConfidence * 100).toFixed(0)}%</span>
        )}
      </div>
    </div>
  );
}

function JsonSection({
  label,
  data,
}: {
  label: string;
  data: unknown[] | unknown;
}) {
  const items = Array.isArray(data) ? data : [];
  if (items.length === 0) return null;

  return (
    <div>
      <h4 className="font-mono text-[10px] text-[#75aafc] uppercase tracking-wider mb-1">
        {label}
      </h4>
      <div className="font-mono text-xs text-[#9bb8d8] bg-[rgba(0,0,0,0.2)] p-2 max-h-32 overflow-y-auto">
        {items.map((item, i) => (
          <div key={i} className="mb-0.5">
            {typeof item === "string" ? item : JSON.stringify(item)}
          </div>
        ))}
      </div>
    </div>
  );
}

export function DirectiveDetail({
  directive,
  onBack,
  onDelete,
}: DirectiveDetailProps) {
  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 gap-2 mb-2">
          <button
            onClick={onBack}
            className="font-mono text-xs text-[#75aafc] hover:text-white transition-colors"
          >
            &larr; Back
          </button>
          <span
            className={`font-mono text-[10px] uppercase ${
              statusColors[directive.status as DirectiveStatus] || "text-[#888]"
            }`}
          >
            {directive.status}
          </span>
          <span className="font-mono text-[10px] text-[#7788aa]">
            v{directive.version}
          </span>
          {onDelete && (
            <button
              onClick={() => onDelete(directive.id)}
              className="ml-auto font-mono text-[10px] text-red-400 hover:text-red-300 transition-colors uppercase"
            >
              Delete
            </button>
          )}
        </div>
        <h2 className="font-mono text-sm text-[#dbe7ff]">
          {directive.title}
        </h2>
      </div>

      {/* Content */}
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {/* Goal */}
        <div>
          <h4 className="font-mono text-[10px] text-[#75aafc] uppercase tracking-wider mb-1">
            Goal
          </h4>
          <p className="font-mono text-xs text-[#9bb8d8] whitespace-pre-wrap">
            {directive.goal}
          </p>
        </div>

        {/* Config */}
        <div className="grid grid-cols-2 gap-2">
          <div>
            <span className="font-mono text-[10px] text-[#7788aa] uppercase">
              Autonomy
            </span>
            <div className="font-mono text-xs text-[#dbe7ff]">
              {directive.autonomyLevel}
            </div>
          </div>
          <div>
            <span className="font-mono text-[10px] text-[#7788aa] uppercase">
              Chains
            </span>
            <div className="font-mono text-xs text-[#dbe7ff]">
              {directive.chainGenerationCount} generated
            </div>
          </div>
          <div>
            <span className="font-mono text-[10px] text-[#7788aa] uppercase">
              Cost
            </span>
            <div className="font-mono text-xs text-[#dbe7ff]">
              ${directive.totalCostUsd.toFixed(2)}
            </div>
          </div>
          {directive.repositoryUrl && (
            <div>
              <span className="font-mono text-[10px] text-[#7788aa] uppercase">
                Repository
              </span>
              <div className="font-mono text-xs text-[#dbe7ff] truncate">
                {directive.repositoryUrl}
              </div>
            </div>
          )}
        </div>

        {/* Structured sections */}
        <JsonSection label="Requirements" data={directive.requirements} />
        <JsonSection
          label="Acceptance Criteria"
          data={directive.acceptanceCriteria}
        />
        <JsonSection label="Constraints" data={directive.constraints} />
        <JsonSection
          label="External Dependencies"
          data={directive.externalDependencies}
        />

        {/* Chains */}
        <div>
          <h4 className="font-mono text-[10px] text-[#75aafc] uppercase tracking-wider mb-2">
            Chains ({directive.chains.length})
          </h4>
          {directive.chains.length === 0 ? (
            <p className="font-mono text-xs text-[#7788aa]">
              No chains yet. Chains are created during planning.
            </p>
          ) : (
            <div className="space-y-2">
              {directive.chains.map((chain) => (
                <ChainCard key={chain.id} chain={chain} />
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}