import { useState, useCallback, useEffect } from "react";
import { useParams, useNavigate } from "react-router";
import { Masthead } from "../components/Masthead";
import { DirectiveList } from "../components/directives/DirectiveList";
import { DirectiveDetail } from "../components/directives/DirectiveDetail";
import { DirectoryInput } from "../components/mesh/DirectoryInput";
import { useDirectives } from "../hooks/useDirectives";
import { useAuth } from "../contexts/AuthContext";
import {
getDaemonDirectories,
getRepositorySuggestions,
} from "../lib/api";
import type {
DirectiveWithChains,
CreateDirectiveRequest,
RepositorySourceType,
DaemonDirectory,
RepositoryHistoryEntry,
} from "../lib/api";
export default function DirectivesPage() {
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]);
if (authLoading) {
return (
);
}
if (isAuthConfigured && !isAuthenticated) {
return null;
}
return ;
}
function DirectivesContent() {
const { id } = useParams<{ id?: string }>();
const navigate = useNavigate();
const {
directives,
loading,
error,
fetchDirective,
saveDirective,
removeDirective,
startDirective,
} = useDirectives();
const [selectedDirective, setSelectedDirective] =
useState(null);
const [detailLoading, setDetailLoading] = useState(false);
const [showCreateForm, setShowCreateForm] = useState(false);
const [createTitle, setCreateTitle] = useState("");
const [createGoal, setCreateGoal] = useState("");
// Repository state
const [repoType, setRepoType] = useState("remote");
const [repoUrl, setRepoUrl] = useState("");
const [repoPath, setRepoPath] = useState("");
const [suggestedDirectories, setSuggestedDirectories] = useState([]);
const [repoSuggestions, setRepoSuggestions] = useState([]);
const [showRepoSuggestions, setShowRepoSuggestions] = useState(false);
// Fetch repository suggestions when modal opens and repo type changes
useEffect(() => {
if (showCreateForm && (repoType === "remote" || repoType === "local")) {
getRepositorySuggestions(repoType, undefined, 10)
.then((res) => {
setRepoSuggestions(res.entries);
setShowRepoSuggestions(res.entries.length > 0);
})
.catch(() => {
setRepoSuggestions([]);
setShowRepoSuggestions(false);
});
} else {
setRepoSuggestions([]);
setShowRepoSuggestions(false);
}
}, [showCreateForm, repoType]);
// Fetch daemon directories when "local" repo type is selected
useEffect(() => {
if (repoType === "local" && showCreateForm) {
getDaemonDirectories()
.then((res) => setSuggestedDirectories(res.directories))
.catch(() => setSuggestedDirectories([]));
}
}, [repoType, showCreateForm]);
// Apply a repository suggestion
const applyRepoSuggestion = useCallback((suggestion: RepositoryHistoryEntry) => {
if (suggestion.repositoryUrl) {
setRepoUrl(suggestion.repositoryUrl);
}
if (suggestion.localPath) {
setRepoPath(suggestion.localPath);
}
setShowRepoSuggestions(false);
}, []);
// Load directive when ID changes
useEffect(() => {
if (id) {
setDetailLoading(true);
fetchDirective(id).then((d) => {
setSelectedDirective(d);
setDetailLoading(false);
});
} else {
setSelectedDirective(null);
}
}, [id, fetchDirective]);
const handleSelect = useCallback(
(directiveId: string) => {
navigate(`/directives/${directiveId}`);
},
[navigate]
);
const handleBack = useCallback(() => {
navigate("/directives");
}, [navigate]);
const resetCreateForm = useCallback(() => {
setShowCreateForm(false);
setCreateTitle("");
setCreateGoal("");
setRepoType("remote");
setRepoUrl("");
setRepoPath("");
}, []);
const handleCreate = useCallback(async () => {
if (!createTitle.trim() || !createGoal.trim()) return;
const data: CreateDirectiveRequest = {
title: createTitle.trim(),
goal: createGoal.trim(),
};
if (repoType === "remote" && repoUrl.trim()) {
data.repositoryUrl = repoUrl.trim();
} else if (repoType === "local" && repoPath.trim()) {
data.localPath = repoPath.trim();
}
const result = await saveDirective(data);
if (result) {
resetCreateForm();
}
}, [createTitle, createGoal, repoType, repoUrl, repoPath, saveDirective, resetCreateForm]);
const handleDelete = useCallback(
async (directiveId: string) => {
const ok = await removeDirective(directiveId);
if (ok && id === directiveId) {
navigate("/directives");
}
},
[removeDirective, id, navigate]
);
const handleStart = useCallback(
async (directiveId: string) => {
const ok = await startDirective(directiveId);
if (ok) {
const updated = await fetchDirective(directiveId);
if (updated) {
setSelectedDirective(updated);
}
}
},
[startDirective, fetchDirective]
);
const handleRefresh = useCallback(
(updated: DirectiveWithChains) => {
setSelectedDirective(updated);
},
[]
);
return (
{error && (
{error}
)}
{/* Create directive modal */}
{showCreateForm && (
New Directive
{/* Title */}
setCreateTitle(e.target.value)}
className="w-full px-3 py-2 font-mono text-sm text-[#dbe7ff] bg-[#0d1b2d] border border-[#3f6fb3] focus:border-[#75aafc] outline-none"
autoFocus
/>
{/* Goal */}
{/* Repository Configuration */}
{/* Repository type selector */}
{/* Repository suggestions */}
{showRepoSuggestions && repoSuggestions.length > 0 && (
{repoSuggestions.map((suggestion) => (
))}
)}
{/* Repository URL (for remote) */}
{repoType === "remote" && (
setRepoUrl(e.target.value)}
placeholder="https://github.com/user/repo.git"
className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
/>
)}
{/* Repository path (for local) */}
{repoType === "local" && (
)}
{/* Actions */}
)}
{/* Directive list */}
setShowCreateForm(true)}
onDelete={(d) => handleDelete(d.id)}
selectedId={id}
/>
{/* Directive detail or empty state */}
{detailLoading ? (
) : selectedDirective ? (
) : (
Select a directive or create a new one
)}
);
}