summaryrefslogblamecommitdiff
path: root/makima/frontend/src/routes/directives.tsx
blob: 90f085406b736afe6be7f9dbfc26eb143dfa03ea (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                         
                                                      
                                                  

                                                                 


                                                  
                         
                
                    


                                                                                     








































                                                                                  

                      









                               
 
                                                      




















































                                                                                              



































                                                                                                     
                                       
























                                                                                                                                                            
import { useState, useCallback, useEffect } from "react";
import { useParams, useNavigate } from "react-router";
import { Masthead } from "../components/Masthead";
import { useDirectives } from "../hooks/useDirectives";
import { useDirectiveDetail } from "../hooks/useDirectiveDetail";
import { useAuth } from "../contexts/AuthContext";
import type {
  DirectiveSummary,
  CreateDirectiveRequest,
  AutonomyLevel,
} from "../lib/api";
import { DirectiveList } from "../components/directives/DirectiveList";
import { DirectiveDetail } from "../components/directives/DirectiveDetail";
import { CreateDirectiveModal } from "../components/directives/CreateDirectiveModal";

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]);

  // 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 <DirectivesPageContent />;
}

function DirectivesPageContent() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const {
    directives,
    loading,
    error,
    createNewDirective,
    archiveExistingDirective,
  } = useDirectives();

  const {
    directive: directiveDetail,
    graph: directiveGraph,
    loading: detailLoading,
    refresh: refreshDetail,
    start: handleStart,
    pause: handlePause,
    resume: handleResume,
    stop: handleStop,
  } = useDirectiveDetail(id);

  const [isCreating, setIsCreating] = useState(false);

  const handleSelect = useCallback(
    (directiveId: string) => {
      navigate(`/directives/${directiveId}`);
    },
    [navigate]
  );

  const handleBack = useCallback(() => {
    navigate("/directives");
  }, [navigate]);

  const handleCreate = useCallback(() => {
    setIsCreating(true);
  }, []);

  const handleCreateSubmit = useCallback(
    async (goal: string, repositoryUrl: string | undefined, autonomyLevel: AutonomyLevel) => {
      const data: CreateDirectiveRequest = {
        goal: goal.trim(),
        repositoryUrl: repositoryUrl?.trim() || undefined,
        autonomyLevel,
      };

      try {
        const result = await createNewDirective(data);
        if (result) {
          setIsCreating(false);
          navigate(`/directives/${result.id}`);
        }
      } catch (err) {
        console.error("Failed to create directive:", err);
      }
    },
    [createNewDirective, navigate]
  );

  const handleCreateCancel = useCallback(() => {
    setIsCreating(false);
  }, []);

  const handleArchive = useCallback(
    async (directive: DirectiveSummary) => {
      if (confirm(`Are you sure you want to archive this directive?`)) {
        const success = await archiveExistingDirective(directive.id);
        if (success && directive.id === id) {
          navigate("/directives");
        }
      }
    },
    [archiveExistingDirective, id, 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 directive modal */}
        {isCreating && (
          <CreateDirectiveModal
            onSubmit={handleCreateSubmit}
            onCancel={handleCreateCancel}
          />
        )}

        <div className="flex-1 grid grid-cols-[350px_1fr] gap-4 min-h-0">
          {/* Directive list */}
          <DirectiveList
            directives={directives}
            loading={loading}
            onSelect={handleSelect}
            onCreate={handleCreate}
            selectedId={id}
            onArchive={handleArchive}
          />

          {/* Directive detail or empty state */}
          {directiveDetail ? (
            <DirectiveDetail
              directive={directiveDetail}
              graph={directiveGraph}
              loading={detailLoading}
              onBack={handleBack}
              onRefresh={refreshDetail}
              onStart={handleStart}
              onPause={handlePause}
              onResume={handleResume}
              onStop={handleStop}
            />
          ) : (
            <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 directive 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 Directive
                </button>
              </div>
            </div>
          )}
        </div>
      </main>
    </div>
  );
}