From 8f725a7c64fbeb85ebeb59b54d2f774e9a0a59d6 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Fri, 6 Feb 2026 01:02:32 +0000
Subject: Fix: Directive page and remove chain page

---
 makima/frontend/src/routes/chains.tsx | 496 ----------------------------------
 1 file changed, 496 deletions(-)
 delete mode 100644 makima/frontend/src/routes/chains.tsx

(limited to 'makima/frontend/src/routes/chains.tsx')

diff --git a/makima/frontend/src/routes/chains.tsx b/makima/frontend/src/routes/chains.tsx
deleted file mode 100644
index 9b33304..0000000
--- a/makima/frontend/src/routes/chains.tsx
+++ /dev/null
@@ -1,496 +0,0 @@
-import { useState, useCallback, useEffect } from "react";
-import { useParams, useNavigate } from "react-router";
-import { Masthead } from "../components/Masthead";
-import { ChainList } from "../components/chains/ChainList";
-import { ChainEditor } from "../components/chains/ChainEditor";
-import { useChains } from "../hooks/useChains";
-import { useAuth } from "../contexts/AuthContext";
-import type {
-  ChainSummary,
-  ChainWithContracts,
-  ChainGraphResponse,
-  CreateChainRequest,
-  AddChainRepositoryRequest,
-  RepositoryHistoryEntry,
-} from "../lib/api";
-import { getRepositorySuggestions } from "../lib/api";
-
-export default function ChainsPage() {
-  const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth();
-  const navigate = useNavigate();
-
-  // Redirect to login if not authenticated (when auth is configured)
-  useEffect(() => {
-    if (!authLoading && isAuthConfigured && !isAuthenticated) {
-      navigate("/login");
-    }
-  }, [authLoading, isAuthConfigured, isAuthenticated, navigate]);
-
-  // Show loading while checking auth
-  if (authLoading) {
-    return (
-      <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
-        <Masthead showNav />
-        <main className="flex-1 flex items-center justify-center">
-          <p className="text-[#7788aa] font-mono text-sm">Loading...</p>
-        </main>
-      </div>
-    );
-  }
-
-  // Don't render if not authenticated (will redirect)
-  if (isAuthConfigured && !isAuthenticated) {
-    return null;
-  }
-
-  return <ChainsPageContent />;
-}
-
-function ChainsPageContent() {
-  const { id } = useParams<{ id: string }>();
-  const navigate = useNavigate();
-  const {
-    chains,
-    loading,
-    error,
-    createNewChain,
-    archiveExistingChain,
-    getChainById,
-    getGraph,
-  } = useChains();
-
-  const [chainDetail, setChainDetail] = useState<ChainWithContracts | null>(null);
-  const [chainGraph, setChainGraph] = useState<ChainGraphResponse | null>(null);
-  const [detailLoading, setDetailLoading] = useState(false);
-  const [isCreating, setIsCreating] = useState(false);
-
-  // Load chain detail when ID changes
-  useEffect(() => {
-    if (id) {
-      setDetailLoading(true);
-      Promise.all([getChainById(id), getGraph(id)]).then(([chain, graph]) => {
-        setChainDetail(chain);
-        setChainGraph(graph);
-        setDetailLoading(false);
-      });
-    } else {
-      setChainDetail(null);
-      setChainGraph(null);
-    }
-  }, [id, getChainById, getGraph]);
-
-  const handleSelect = useCallback(
-    (chainId: string) => {
-      navigate(`/chains/${chainId}`);
-    },
-    [navigate]
-  );
-
-  const handleBack = useCallback(() => {
-    navigate("/chains");
-  }, [navigate]);
-
-  const handleCreate = useCallback(() => {
-    setIsCreating(true);
-  }, []);
-
-  const handleCreateSubmit = useCallback(
-    async (name: string, description: string, repositories: AddChainRepositoryRequest[]) => {
-      const data: CreateChainRequest = {
-        name: name.trim(),
-        description: description.trim() || undefined,
-        repositories: repositories.length > 0 ? repositories : undefined,
-      };
-
-      try {
-        const result = await createNewChain(data);
-        if (result) {
-          setIsCreating(false);
-          navigate(`/chains/${result.id}`);
-        }
-      } catch (err) {
-        console.error("Failed to create chain:", err);
-      }
-    },
-    [createNewChain, navigate]
-  );
-
-  const handleCreateCancel = useCallback(() => {
-    setIsCreating(false);
-  }, []);
-
-  const handleArchive = useCallback(
-    async (chain: ChainSummary) => {
-      if (confirm(`Are you sure you want to archive "${chain.name}"?`)) {
-        const success = await archiveExistingChain(chain.id);
-        if (success && chain.id === id) {
-          navigate("/chains");
-        }
-      }
-    },
-    [archiveExistingChain, id, navigate]
-  );
-
-  const handleRefresh = useCallback(async () => {
-    if (id) {
-      const [chain, graph] = await Promise.all([getChainById(id), getGraph(id)]);
-      setChainDetail(chain);
-      setChainGraph(graph);
-    }
-  }, [id, getChainById, getGraph]);
-
-  const handleContractClick = useCallback(
-    (contractId: string) => {
-      navigate(`/contracts/${contractId}`);
-    },
-    [navigate]
-  );
-
-  return (
-    <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
-      <Masthead showNav />
-      <main className="flex-1 flex flex-col p-4 pt-2 gap-4 overflow-hidden">
-        {error && (
-          <div className="p-3 bg-red-400/10 border border-red-400/30 text-red-400 font-mono text-sm">
-            {error}
-          </div>
-        )}
-
-        {/* Create chain modal */}
-        {isCreating && (
-          <CreateChainModal
-            onSubmit={handleCreateSubmit}
-            onCancel={handleCreateCancel}
-          />
-        )}
-
-        <div className="flex-1 grid grid-cols-[350px_1fr] gap-4 min-h-0">
-          {/* Chain list */}
-          <ChainList
-            chains={chains}
-            loading={loading}
-            onSelect={handleSelect}
-            onCreate={handleCreate}
-            selectedId={id}
-            onArchive={handleArchive}
-          />
-
-          {/* Chain detail/editor or empty state */}
-          {chainDetail ? (
-            <ChainEditor
-              chain={chainDetail}
-              graph={chainGraph}
-              loading={detailLoading}
-              onBack={handleBack}
-              onRefresh={handleRefresh}
-              onContractClick={handleContractClick}
-            />
-          ) : (
-            <div className="panel h-full flex items-center justify-center">
-              <div className="text-center">
-                <p className="font-mono text-sm text-[#555] mb-4">
-                  Select a chain or create a new one
-                </p>
-                <button
-                  onClick={handleCreate}
-                  className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
-                >
-                  + New Chain
-                </button>
-              </div>
-            </div>
-          )}
-        </div>
-      </main>
-    </div>
-  );
-}
-
-interface CreateChainModalProps {
-  onSubmit: (name: string, description: string, repositories: AddChainRepositoryRequest[]) => void;
-  onCancel: () => void;
-}
-
-type RepoMode = "remote" | "local" | null;
-
-function CreateChainModal({ onSubmit, onCancel }: CreateChainModalProps) {
-  const [name, setName] = useState("");
-  const [description, setDescription] = useState("");
-  const [repositories, setRepositories] = useState<AddChainRepositoryRequest[]>([]);
-
-  // Repository input state
-  const [repoMode, setRepoMode] = useState<RepoMode>(null);
-  const [repoName, setRepoName] = useState("");
-  const [repoUrl, setRepoUrl] = useState("");
-  const [repoPath, setRepoPath] = useState("");
-
-  // Suggestions
-  const [suggestions, setSuggestions] = useState<RepositoryHistoryEntry[]>([]);
-  const [showSuggestions, setShowSuggestions] = useState(false);
-
-  // Load suggestions when mode changes
-  useEffect(() => {
-    if (repoMode) {
-      getRepositorySuggestions(repoMode, undefined, 10)
-        .then((res) => {
-          setSuggestions(res.entries);
-          setShowSuggestions(res.entries.length > 0);
-        })
-        .catch(() => {
-          setSuggestions([]);
-          setShowSuggestions(false);
-        });
-    } else {
-      setSuggestions([]);
-      setShowSuggestions(false);
-    }
-  }, [repoMode]);
-
-  const applySuggestion = (suggestion: RepositoryHistoryEntry) => {
-    setRepoName(suggestion.name);
-    if (suggestion.repositoryUrl) setRepoUrl(suggestion.repositoryUrl);
-    if (suggestion.localPath) setRepoPath(suggestion.localPath);
-    setShowSuggestions(false);
-  };
-
-  const handleAddRepo = () => {
-    if (!repoName.trim()) return;
-    if (repoMode === "remote" && !repoUrl.trim()) return;
-    if (repoMode === "local" && !repoPath.trim()) return;
-
-    const newRepo: AddChainRepositoryRequest = {
-      name: repoName.trim(),
-      sourceType: repoMode || "remote",
-      isPrimary: repositories.length === 0, // First one is primary
-      ...(repoMode === "remote" ? { repositoryUrl: repoUrl.trim() } : { localPath: repoPath.trim() }),
-    };
-
-    setRepositories([...repositories, newRepo]);
-    setRepoMode(null);
-    setRepoName("");
-    setRepoUrl("");
-    setRepoPath("");
-  };
-
-  const handleRemoveRepo = (index: number) => {
-    const newRepos = repositories.filter((_, i) => i !== index);
-    // If we removed the primary, make the first one primary
-    if (newRepos.length > 0 && repositories[index]?.isPrimary) {
-      newRepos[0].isPrimary = true;
-    }
-    setRepositories(newRepos);
-  };
-
-  const handleSubmit = () => {
-    if (name.trim()) {
-      onSubmit(name.trim(), description.trim(), repositories);
-    }
-  };
-
-  return (
-    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
-      <div className="w-full max-w-lg p-6 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] max-h-[90vh] overflow-y-auto">
-        <h3 className="font-mono text-sm text-[#75aafc] uppercase mb-4">
-          Create Chain
-        </h3>
-
-        <div className="space-y-4">
-          {/* Chain name */}
-          <div>
-            <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
-              Chain Name *
-            </label>
-            <input
-              type="text"
-              value={name}
-              onChange={(e) => setName(e.target.value)}
-              placeholder="e.g., Feature Implementation"
-              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>
-
-          {/* Description */}
-          <div>
-            <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
-              Description (optional)
-            </label>
-            <textarea
-              value={description}
-              onChange={(e) => setDescription(e.target.value)}
-              placeholder="Describe what this chain accomplishes..."
-              rows={2}
-              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>
-
-          {/* Repositories */}
-          <div>
-            <label className="block font-mono text-xs text-[#8b949e] uppercase mb-2">
-              Repositories
-            </label>
-
-            {/* Added repositories */}
-            {repositories.length > 0 && (
-              <div className="space-y-2 mb-3">
-                {repositories.map((repo, index) => (
-                  <div
-                    key={index}
-                    className="flex items-center gap-2 px-3 py-2 bg-[#0d1b2d] border border-[rgba(117,170,252,0.2)]"
-                  >
-                    <span className="font-mono text-[10px] text-[#556677] uppercase">
-                      {repo.sourceType === "remote" ? "URL" : "Local"}
-                    </span>
-                    <span className="font-mono text-xs text-[#dbe7ff] flex-1 truncate">
-                      {repo.name}
-                    </span>
-                    {repo.isPrimary && (
-                      <span className="font-mono text-[8px] text-[#75aafc] uppercase px-1 border border-[#75aafc]/30">
-                        primary
-                      </span>
-                    )}
-                    <button
-                      onClick={() => handleRemoveRepo(index)}
-                      className="font-mono text-xs text-[#556677] hover:text-red-400"
-                    >
-                      ✕
-                    </button>
-                  </div>
-                ))}
-              </div>
-            )}
-
-            {/* Add repository form */}
-            {repoMode ? (
-              <div className="p-3 border border-[rgba(117,170,252,0.3)] space-y-3">
-                <div className="flex items-center justify-between">
-                  <span className="font-mono text-[10px] text-[#75aafc] uppercase">
-                    Add {repoMode === "remote" ? "Remote" : "Local"} Repository
-                  </span>
-                  {suggestions.length > 0 && (
-                    <button
-                      onClick={() => setShowSuggestions(!showSuggestions)}
-                      className="font-mono text-[10px] text-[#556677] hover:text-[#9bc3ff]"
-                    >
-                      {showSuggestions ? "Hide" : `${suggestions.length} suggestions`}
-                    </button>
-                  )}
-                </div>
-
-                {/* Suggestions dropdown */}
-                {showSuggestions && suggestions.length > 0 && (
-                  <div className="border border-[rgba(117,170,252,0.2)] bg-[#0a1525] max-h-28 overflow-y-auto">
-                    {suggestions.map((s) => (
-                      <button
-                        key={s.id}
-                        onClick={() => applySuggestion(s)}
-                        className="w-full text-left px-3 py-2 font-mono text-xs hover:bg-[rgba(117,170,252,0.1)] border-b border-[rgba(117,170,252,0.1)] last:border-b-0"
-                      >
-                        <div className="flex items-center justify-between">
-                          <span className="text-[#9bc3ff] truncate">{s.name}</span>
-                          <span className="text-[10px] text-[#556677]">{s.useCount}×</span>
-                        </div>
-                        <div className="text-[10px] text-[#556677] truncate">
-                          {repoMode === "local" ? s.localPath : s.repositoryUrl}
-                        </div>
-                      </button>
-                    ))}
-                  </div>
-                )}
-
-                <input
-                  type="text"
-                  value={repoName}
-                  onChange={(e) => setRepoName(e.target.value)}
-                  placeholder="Repository name"
-                  className="w-full px-3 py-2 bg-[#050d18] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-xs focus:outline-none focus:border-[#75aafc]"
-                />
-
-                {repoMode === "remote" ? (
-                  <input
-                    type="text"
-                    value={repoUrl}
-                    onChange={(e) => setRepoUrl(e.target.value)}
-                    placeholder="https://github.com/owner/repo"
-                    className="w-full px-3 py-2 bg-[#050d18] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-xs focus:outline-none focus:border-[#75aafc]"
-                  />
-                ) : (
-                  <input
-                    type="text"
-                    value={repoPath}
-                    onChange={(e) => setRepoPath(e.target.value)}
-                    placeholder="/path/to/repository"
-                    className="w-full px-3 py-2 bg-[#050d18] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-xs focus:outline-none focus:border-[#75aafc]"
-                  />
-                )}
-
-                <div className="flex gap-2">
-                  <button
-                    onClick={() => setRepoMode(null)}
-                    className="px-3 py-1.5 font-mono text-xs text-[#556677] hover:text-[#9bc3ff]"
-                  >
-                    Cancel
-                  </button>
-                  <button
-                    onClick={handleAddRepo}
-                    disabled={
-                      !repoName.trim() ||
-                      (repoMode === "remote" && !repoUrl.trim()) ||
-                      (repoMode === "local" && !repoPath.trim())
-                    }
-                    className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] disabled:opacity-50"
-                  >
-                    Add
-                  </button>
-                </div>
-              </div>
-            ) : (
-              <div className="flex gap-2">
-                <button
-                  onClick={() => setRepoMode("remote")}
-                  className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3]"
-                >
-                  + Remote
-                </button>
-                <button
-                  onClick={() => setRepoMode("local")}
-                  className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3]"
-                >
-                  + Local
-                </button>
-              </div>
-            )}
-
-            {repositories.length === 0 && !repoMode && (
-              <p className="font-mono text-[10px] text-[#556677] mt-2">
-                Add repositories that contracts in this chain will work with
-              </p>
-            )}
-          </div>
-
-          <p className="font-mono text-xs text-[#8b949e]">
-            A chain links multiple contracts together in a DAG. Contracts depend on each
-            other and start automatically when dependencies complete.
-          </p>
-
-          {/* Actions */}
-          <div className="flex gap-2 justify-end pt-2">
-            <button
-              onClick={onCancel}
-              className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
-            >
-              Cancel
-            </button>
-            <button
-              onClick={handleSubmit}
-              disabled={!name.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>
-  );
-}
-- 
cgit v1.2.3

