summaryrefslogblamecommitdiff
path: root/makima/frontend/src/components/contracts/ContractDetail.tsx
blob: 46b221263a2d5b7b61be492de08a67aa5f285850 (plain) (tree)



















                                                                  
                                                      


















































































                                                                                 
                                                        









































































                                                                                                                                                                      




                                                                                                                                        









































                                                                                         
                                                          





                                                   
                                 




























                                                        
                                                





















                                   
            




                                                                     
                        


                               

                                                                   
 















































































































































































































































































































































































                                                                                                                                                                                                
               





                                     
                       







































                                                                                                     

                                                                                                  



























































































                                                                                                                                                                                                      
import { useState, useEffect, useCallback } from "react";
import type {
  ContractWithRelations,
  ContractPhase,
  ContractStatus,
  ContractRepository,
  FileSummary,
  TaskSummary,
  TemplateSummary,
} from "../../lib/api";
import {
  listTemplates,
  getTemplate,
  createFile,
} from "../../lib/api";
import { PhaseProgressBar } from "./PhaseProgressBar";
import { PhaseHint } from "./PhaseHint";
import { RepositoryPanel } from "./RepositoryPanel";
import { ContractCliInput } from "./ContractCliInput";
import { PhaseDeliverablesPanel } from "./PhaseDeliverablesPanel";
import { CommandModePanel } from "./CommandModePanel";
import { TaskTree } from "../mesh/TaskTree";

type Tab = "overview" | "repos" | "files" | "tasks";

interface ContractDetailProps {
  contract: ContractWithRelations;
  loading: boolean;
  onBack: () => void;
  onUpdate: (name: string, description: string) => void;
  onDelete: () => void;
  onPhaseChange: (phase: ContractPhase) => void;
  onStatusChange: (status: ContractStatus) => void;
  onFileSelect: (id: string) => void;
  onTaskSelect: (id: string) => void;
  onTaskCreate: (name: string, plan: string, repositoryUrl?: string) => void;
  onRefresh: () => void;
  // Repository callbacks
  onAddRemoteRepo: (name: string, url: string, isPrimary: boolean) => void;
  onAddLocalRepo: (name: string, path: string, isPrimary: boolean) => void;
  onCreateManagedRepo: (name: string, isPrimary: boolean) => void;
  onDeleteRepo: (repoId: string) => void;
  onSetRepoPrimary: (repoId: string) => void;
  // File creation callback for phase deliverables
  onCreateFileFromTemplate?: (templateId: string, suggestedName: string) => void;
}

const statusConfig: Record<ContractStatus, { label: string; color: string }> = {
  active: { label: "Active", color: "text-green-400" },
  completed: { label: "Completed", color: "text-blue-400" },
  archived: { label: "Archived", color: "text-[#555]" },
};

export function ContractDetail({
  contract,
  loading,
  onBack,
  onUpdate,
  onDelete,
  onPhaseChange,
  onStatusChange,
  onFileSelect,
  onTaskSelect,
  onTaskCreate,
  onRefresh,
  onAddRemoteRepo,
  onAddLocalRepo,
  onCreateManagedRepo,
  onDeleteRepo,
  onSetRepoPrimary,
  onCreateFileFromTemplate,
}: ContractDetailProps) {
  const [activeTab, setActiveTab] = useState<Tab>("overview");
  const [isEditing, setIsEditing] = useState(false);
  const [name, setName] = useState(contract.name);
  const [description, setDescription] = useState(contract.description || "");

  const handleSave = () => {
    onUpdate(name, description);
    setIsEditing(false);
  };

  const handleCancel = () => {
    setName(contract.name);
    setDescription(contract.description || "");
    setIsEditing(false);
  };

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

  const tabs: { key: Tab; label: string; count?: number }[] = [
    { key: "overview", label: "Overview" },
    { key: "repos", label: "Repositories", count: contract.repositories.length },
    { key: "files", label: "Files", count: contract.files.length },
    { key: "tasks", label: "Tasks", count: contract.tasks.length },
  ];

  return (
    <div className="panel h-full flex flex-col min-h-0">
      {/* 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">
          <button
            onClick={onBack}
            className="font-mono text-xs text-[#75aafc] hover:text-[#9bc3ff] transition-colors"
          >
            &larr; Back to list
          </button>
          <div className="flex items-center gap-2">
            {isEditing ? (
              <>
                <button
                  onClick={handleCancel}
                  className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors uppercase"
                >
                  Cancel
                </button>
                <button
                  onClick={handleSave}
                  className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
                >
                  Save
                </button>
              </>
            ) : (
              <>
                <button
                  onClick={() => setIsEditing(true)}
                  className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] border border-[#0f3c78] hover:border-[#3f6fb3] transition-colors uppercase"
                >
                  Edit
                </button>
                <button
                  onClick={onDelete}
                  className="px-3 py-1.5 font-mono text-xs text-red-400 border border-red-400/30 hover:border-red-400/50 transition-colors uppercase"
                >
                  Delete
                </button>
              </>
            )}
          </div>
        </div>

        {isEditing ? (
          <div className="space-y-3">
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
              placeholder="Contract name"
            />
            <textarea
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc] resize-none"
              rows={2}
              placeholder="Description (optional)"
            />
          </div>
        ) : (
          <>
            <div className="flex items-center gap-3 mb-2">
              <h2 className="font-mono text-lg text-[#dbe7ff]">
                {contract.name}
              </h2>
              <span
                className={`font-mono text-xs uppercase ${
                  statusConfig[contract.status].color
                }`}
              >
                {statusConfig[contract.status].label}
              </span>
              {contract.localOnly && (
                <span className="px-2 py-0.5 font-mono text-[10px] uppercase text-amber-400 border border-amber-400/30 bg-amber-400/10">
                  Local-Only
                </span>
              )}
            </div>
            {contract.description && (
              <p className="font-mono text-sm text-[#9bc3ff] mb-3">
                {contract.description}
              </p>
            )}
          </>
        )}

        {/* Phase progress */}
        <div className="mt-4 pt-4 border-t border-dashed border-[rgba(117,170,252,0.2)]">
          <PhaseProgressBar
            currentPhase={contract.phase}
            onPhaseClick={onPhaseChange}
          />
        </div>
      </div>

      {/* Tabs */}
      <div className="flex border-b border-[rgba(117,170,252,0.2)]">
        {tabs.map((tab) => (
          <button
            key={tab.key}
            onClick={() => setActiveTab(tab.key)}
            className={`
              px-4 py-2 font-mono text-xs uppercase tracking-wider transition-colors
              ${
                activeTab === tab.key
                  ? "text-[#dbe7ff] border-b-2 border-[#75aafc]"
                  : "text-[#555] hover:text-[#9bc3ff]"
              }
            `}
          >
            {tab.label}
            {tab.count !== undefined && tab.count > 0 && (
              <span className="ml-1 text-[10px]">({tab.count})</span>
            )}
          </button>
        ))}
      </div>

      {/* Tab content */}
      <div className="flex-1 overflow-y-auto p-4 min-h-0">
        {activeTab === "overview" && (
          <OverviewTab
            contract={contract}
            onStatusChange={onStatusChange}
            onPhaseChange={onPhaseChange}
            onCreateFile={onCreateFileFromTemplate}
            onRefresh={onRefresh}
          />
        )}

        {activeTab === "repos" && (
          <RepositoryPanel
            repositories={contract.repositories}
            onAddRemote={onAddRemoteRepo}
            onAddLocal={onAddLocalRepo}
            onCreateManaged={onCreateManagedRepo}
            onDelete={onDeleteRepo}
            onSetPrimary={onSetRepoPrimary}
          />
        )}

        {activeTab === "files" && (
          <FilesTab
            files={contract.files}
            contractId={contract.id}
            contractPhase={contract.phase}
            onSelect={onFileSelect}
            onRefresh={onRefresh}
          />
        )}

        {activeTab === "tasks" && (
          <TasksTab
            tasks={contract.tasks}
            repositories={contract.repositories}
            supervisorTaskId={contract.supervisorTaskId}
            contractType={contract.contractType}
            onSelect={onTaskSelect}
            onCreate={onTaskCreate}
          />
        )}
      </div>

      {/* Chat Input */}
      <ContractCliInput
        contractId={contract.id}
        contract={contract}
        onUpdate={onRefresh}
      />
    </div>
  );
}

// Overview tab
function OverviewTab({
  contract,
  onStatusChange,
  onPhaseChange,
  onCreateFile,
  onRefresh,
}: {
  contract: ContractWithRelations;
  onStatusChange: (status: ContractStatus) => void;
  onPhaseChange: (phase: ContractPhase) => void;
  onCreateFile?: (templateId: string, suggestedName: string) => void;
  onRefresh: () => void;
}) {
  return (
    <div className="space-y-6">
      {/* Command Mode controls */}
      <CommandModePanel contract={contract} onUpdate={onRefresh} />

      {/* Phase deliverables checklist */}
      <PhaseDeliverablesPanel
        contract={contract}
        onCreateFile={onCreateFile}
      />

      {/* Phase hint */}
      <PhaseHint contract={contract} onAdvancePhase={onPhaseChange} />

      {/* Task progress summary */}
      <TaskStatusSummary tasks={contract.tasks} />

      {/* Stats */}
      <div className="grid grid-cols-3 gap-4">
        <StatCard label="Repositories" value={contract.repositories.length} />
        <StatCard label="Files" value={contract.files.length} />
        <StatCard label="Tasks" value={contract.tasks.length} />
      </div>

      {/* Status change */}
      <div>
        <h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">
          Status
        </h3>
        <div className="flex gap-2">
          {(["active", "completed", "archived"] as ContractStatus[]).map(
            (status) => (
              <button
                key={status}
                onClick={() => onStatusChange(status)}
                className={`
                  px-3 py-1.5 font-mono text-xs uppercase transition-colors
                  ${
                    contract.status === status
                      ? "bg-[rgba(117,170,252,0.1)] text-[#9bc3ff] border border-[rgba(117,170,252,0.3)]"
                      : "text-[#555] border border-transparent hover:text-[#75aafc]"
                  }
                `}
              >
                {status}
              </button>
            )
          )}
        </div>
      </div>

      {/* Metadata */}
      <div>
        <h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">
          Details
        </h3>
        <div className="space-y-1 font-mono text-xs text-[#555]">
          <p>Created: {new Date(contract.createdAt).toLocaleString()}</p>
          <p>Updated: {new Date(contract.updatedAt).toLocaleString()}</p>
          <p>Version: {contract.version}</p>
        </div>
      </div>
    </div>
  );
}

function StatCard({ label, value }: { label: string; value: number }) {
  return (
    <div className="p-3 border border-[rgba(117,170,252,0.2)]">
      <div className="font-mono text-2xl text-[#dbe7ff]">{value}</div>
      <div className="font-mono text-[10px] text-[#555] uppercase">{label}</div>
    </div>
  );
}

// Task status summary with progress bar
function TaskStatusSummary({ tasks }: { tasks: TaskSummary[] }) {
  if (tasks.length === 0) return null;

  // Count tasks by status
  const statusCounts = {
    done: 0,
    merged: 0,
    running: 0,
    pending: 0,
    failed: 0,
    other: 0,
  };

  for (const task of tasks) {
    switch (task.status) {
      case "done":
        statusCounts.done++;
        break;
      case "merged":
        statusCounts.merged++;
        break;
      case "running":
      case "initializing":
      case "starting":
        statusCounts.running++;
        break;
      case "pending":
        statusCounts.pending++;
        break;
      case "failed":
        statusCounts.failed++;
        break;
      default:
        statusCounts.other++;
    }
  }

  const completedCount = statusCounts.done + statusCounts.merged;
  const progressPercent = (completedCount / tasks.length) * 100;

  // Build summary parts
  const parts: string[] = [];
  if (completedCount > 0) parts.push(`${completedCount} done`);
  if (statusCounts.running > 0) parts.push(`${statusCounts.running} running`);
  if (statusCounts.pending > 0) parts.push(`${statusCounts.pending} pending`);
  if (statusCounts.failed > 0) parts.push(`${statusCounts.failed} failed`);

  return (
    <div className="space-y-2">
      <h3 className="font-mono text-xs text-[#75aafc] uppercase">
        Task Progress
      </h3>

      {/* Progress bar */}
      <div className="h-2 bg-[rgba(117,170,252,0.1)] rounded overflow-hidden">
        <div
          className="h-full bg-green-400 transition-all duration-300"
          style={{ width: `${progressPercent}%` }}
        />
      </div>

      {/* Summary text */}
      <div className="flex items-center justify-between">
        <span className="font-mono text-xs text-[#9bc3ff]">
          {parts.join(", ")}
        </span>
        <span className="font-mono text-xs text-[#555]">
          {completedCount}/{tasks.length} completed
        </span>
      </div>
    </div>
  );
}

// Phase color mapping for badges
const phaseColors: Record<ContractPhase, string> = {
  research: "bg-purple-500/20 text-purple-400 border-purple-400/30",
  specify: "bg-blue-500/20 text-blue-400 border-blue-400/30",
  plan: "bg-cyan-500/20 text-cyan-400 border-cyan-400/30",
  execute: "bg-green-500/20 text-green-400 border-green-400/30",
  review: "bg-yellow-500/20 text-yellow-400 border-yellow-400/30",
};

// Files tab with template creation
function FilesTab({
  files,
  contractId,
  contractPhase,
  onSelect,
  onRefresh,
}: {
  files: FileSummary[];
  contractId: string;
  contractPhase: ContractPhase;
  onSelect: (id: string) => void;
  onRefresh: () => void;
}) {
  const [showTemplateModal, setShowTemplateModal] = useState(false);
  const [templates, setTemplates] = useState<TemplateSummary[]>([]);
  const [loadingTemplates, setLoadingTemplates] = useState(false);
  const [creating, setCreating] = useState(false);
  const [fileName, setFileName] = useState("");
  const [selectedTemplateId, setSelectedTemplateId] = useState<string | null>(null);

  // Load templates when modal opens
  useEffect(() => {
    if (showTemplateModal) {
      setLoadingTemplates(true);
      listTemplates(contractPhase)
        .then((res) => setTemplates(res.templates))
        .catch((err) => console.error("Failed to load templates:", err))
        .finally(() => setLoadingTemplates(false));
    }
  }, [showTemplateModal, contractPhase]);

  const handleCreateFromTemplate = useCallback(async () => {
    if (!fileName.trim() || !selectedTemplateId) return;

    setCreating(true);
    try {
      // Get the full template with body
      const template = await getTemplate(selectedTemplateId);

      // Create the file with contract (files must belong to contracts)
      await createFile({
        contractId,
        name: fileName.trim(),
        description: template.description,
        body: template.suggestedBody,
      });

      // Reset and close
      setShowTemplateModal(false);
      setFileName("");
      setSelectedTemplateId(null);
      onRefresh();
    } catch (err) {
      console.error("Failed to create file from template:", err);
    } finally {
      setCreating(false);
    }
  }, [fileName, selectedTemplateId, contractId, onRefresh]);

  const handleCloseModal = () => {
    setShowTemplateModal(false);
    setFileName("");
    setSelectedTemplateId(null);
  };

  return (
    <div className="space-y-4">
      {/* Create from template button */}
      <button
        onClick={() => setShowTemplateModal(true)}
        className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors"
      >
        + Create from Template
      </button>

      {/* Template Selection Modal */}
      {showTemplateModal && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
          <div className="w-full max-w-lg p-6 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] max-h-[80vh] flex flex-col">
            <div className="flex items-center justify-between mb-4">
              <h3 className="font-mono text-sm text-[#75aafc] uppercase">
                Create File from Template
              </h3>
              <span className={`px-2 py-0.5 text-[10px] font-mono uppercase border rounded ${phaseColors[contractPhase]}`}>
                {contractPhase} phase
              </span>
            </div>

            <div className="space-y-4 flex-1 overflow-y-auto">
              {/* File name input */}
              <div>
                <label className="block font-mono text-xs text-[#555] uppercase mb-1">
                  File Name
                </label>
                <input
                  type="text"
                  value={fileName}
                  onChange={(e) => setFileName(e.target.value)}
                  placeholder="e.g., Project Requirements"
                  className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
                  autoFocus
                />
              </div>

              {/* Template selection */}
              <div>
                <label className="block font-mono text-xs text-[#555] uppercase mb-2">
                  Select Template
                </label>
                {loadingTemplates ? (
                  <p className="font-mono text-xs text-[#555]">Loading templates...</p>
                ) : templates.length === 0 ? (
                  <p className="font-mono text-xs text-[#555]">No templates available for {contractPhase} phase</p>
                ) : (
                  <div className="space-y-2 max-h-60 overflow-y-auto">
                    {templates.map((template) => (
                      <button
                        key={template.id}
                        onClick={() => setSelectedTemplateId(template.id)}
                        className={`w-full text-left p-3 border transition-colors ${
                          selectedTemplateId === template.id
                            ? "border-[#75aafc] bg-[rgba(117,170,252,0.1)]"
                            : "border-[rgba(117,170,252,0.2)] hover:border-[rgba(117,170,252,0.4)]"
                        }`}
                      >
                        <div className="flex items-center justify-between mb-1">
                          <span className="font-mono text-sm text-[#dbe7ff]">
                            {template.name}
                          </span>
                          <span className="font-mono text-[10px] text-[#555]">
                            {template.elementCount} elements
                          </span>
                        </div>
                        <p className="font-mono text-xs text-[#555]">
                          {template.description}
                        </p>
                      </button>
                    ))}
                  </div>
                )}
              </div>
            </div>

            <div className="flex gap-2 justify-end mt-4 pt-4 border-t border-[rgba(117,170,252,0.2)]">
              <button
                onClick={handleCloseModal}
                className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={handleCreateFromTemplate}
                disabled={!fileName.trim() || !selectedTemplateId || creating}
                className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
              >
                {creating ? "Creating..." : "Create File"}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* File list */}
      {files.length === 0 ? (
        <p className="font-mono text-xs text-[#555]">
          No files in this contract. Create one from a template above.
        </p>
      ) : (
        <div className="space-y-2">
          {files.map((file) => (
            <button
              key={file.id}
              onClick={() => onSelect(file.id)}
              className="w-full text-left p-3 border border-[rgba(117,170,252,0.2)] hover:border-[rgba(117,170,252,0.4)] transition-colors"
            >
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <span className="font-mono text-sm text-[#dbe7ff]">
                    {file.name}
                  </span>
                  {file.contractPhase && (
                    <span
                      className={`px-1.5 py-0.5 text-[9px] font-mono uppercase border rounded ${
                        phaseColors[file.contractPhase]
                      }`}
                      title={`Added during ${file.contractPhase} phase`}
                    >
                      {file.contractPhase}
                    </span>
                  )}
                </div>
                <span className="font-mono text-[10px] text-[#555]">
                  v{file.version}
                </span>
              </div>
              {file.description && (
                <p className="font-mono text-xs text-[#555] mt-1 truncate">
                  {file.description}
                </p>
              )}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// Tasks tab - now using TaskTree for supervisor view
function TasksTab({
  tasks,
  repositories,
  supervisorTaskId,
  contractType,
  onSelect,
  onCreate,
}: {
  tasks: TaskSummary[];
  repositories: ContractRepository[];
  supervisorTaskId: string | null;
  contractType: string;
  onSelect: (id: string) => void;
  onCreate: (name: string, plan: string, repositoryUrl?: string) => void;
}) {
  const [isCreating, setIsCreating] = useState(false);
  const [taskName, setTaskName] = useState("");
  const [taskPlan, setTaskPlan] = useState("# Plan\n\nDescribe what this task should accomplish...");

  // Find primary repository or first ready one
  const readyRepos = repositories.filter((r) => r.status === "ready");
  const primaryRepo = readyRepos.find((r) => r.isPrimary) || readyRepos[0];
  const [selectedRepoId, setSelectedRepoId] = useState<string>(primaryRepo?.id || "");

  const handleCreate = () => {
    if (!taskName.trim()) return;
    const selectedRepo = repositories.find((r) => r.id === selectedRepoId);
    // Get the URL - for remote repos it's repositoryUrl, for local it's the local path
    const repoUrl = selectedRepo?.repositoryUrl || selectedRepo?.localPath;
    onCreate(taskName.trim(), taskPlan, repoUrl || undefined);
    setIsCreating(false);
    setTaskName("");
    setTaskPlan("# Plan\n\nDescribe what this task should accomplish...");
    setSelectedRepoId(primaryRepo?.id || "");
  };

  const handleCancel = () => {
    setIsCreating(false);
    setTaskName("");
    setTaskPlan("# Plan\n\nDescribe what this task should accomplish...");
    setSelectedRepoId(primaryRepo?.id || "");
  };

  return (
    <div className="space-y-4">
      {/* TaskTree with supervisor view */}
      <TaskTree
        tasks={tasks}
        supervisorTaskId={supervisorTaskId}
        onSelect={onSelect}
      />

      {/* Manual task creation - show for task-type contracts or contracts without supervisors */}
      {(contractType === "task" || !supervisorTaskId) && (
        <>
          <div className="border-t border-[rgba(117,170,252,0.2)] pt-4">
            <button
              onClick={() => setIsCreating(true)}
              className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors"
            >
              + Create Task Manually
            </button>
          </div>

          {/* Create Task Modal */}
          {isCreating && (
            <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
              <div className="w-full max-w-lg p-6 bg-[#0a1628] border border-[rgba(117,170,252,0.3)]">
                <h3 className="font-mono text-sm text-[#75aafc] uppercase mb-4">
                  Create Task
                </h3>
                <div className="space-y-4">
                  <div>
                    <label className="block font-mono text-xs text-[#555] uppercase mb-1">
                      Name
                    </label>
                    <input
                      type="text"
                      value={taskName}
                      onChange={(e) => setTaskName(e.target.value)}
                      placeholder="Task name"
                      className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
                      autoFocus
                    />
                  </div>

                  {/* Repository selection */}
                  {readyRepos.length > 0 && (
                    <div>
                      <label className="block font-mono text-xs text-[#555] uppercase mb-1">
                        Repository
                      </label>
                      <select
                        value={selectedRepoId}
                        onChange={(e) => setSelectedRepoId(e.target.value)}
                        className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
                      >
                        <option value="">No repository</option>
                        {readyRepos.map((repo) => (
                          <option key={repo.id} value={repo.id}>
                            {repo.name}
                            {repo.isPrimary ? " (Primary)" : ""}
                            {" - "}
                            {repo.sourceType}
                          </option>
                        ))}
                      </select>
                    </div>
                  )}

                  <div>
                    <label className="block font-mono text-xs text-[#555] uppercase mb-1">
                      Plan
                    </label>
                    <textarea
                      value={taskPlan}
                      onChange={(e) => setTaskPlan(e.target.value)}
                      rows={6}
                      className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc] resize-none"
                    />
                  </div>

                  <div className="flex gap-2 justify-end">
                    <button
                      onClick={handleCancel}
                      className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
                    >
                      Cancel
                    </button>
                    <button
                      onClick={handleCreate}
                      disabled={!taskName.trim()}
                      className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
                    >
                      Create
                    </button>
                  </div>
                </div>
              </div>
            </div>
          )}
        </>
      )}
    </div>
  );
}