summaryrefslogblamecommitdiff
path: root/makima/frontend/src/routes/tmp.tsx
blob: 69f13a2ae09769ed928f585765bad30ee531cd67 (plain) (tree)




























































































                                                                                                                
/**
 * Standalone task page for orphan tasks (`/tmp/:taskId`). These are tasks
 * with no directive attachment — the document-mode sidebar surfaces them
 * under the `tmp/` pseudo-folder. We render `DocumentTaskStream` directly
 * without the directive sidebar selection, framed by the masthead so users
 * still have global navigation.
 */
import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router";
import { Masthead } from "../components/Masthead";
import { DocumentTaskStream } from "../components/directives/DocumentTaskStream";
import { useAuth } from "../contexts/AuthContext";
import { getTask, type Task } from "../lib/api";

export default function TmpTaskPage() {
  const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth();
  const navigate = useNavigate();
  const { taskId } = useParams<{ taskId: string }>();
  const [task, setTask] = useState<Task | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!authLoading && isAuthConfigured && !isAuthenticated) {
      navigate("/login");
    }
  }, [authLoading, isAuthConfigured, isAuthenticated, navigate]);

  useEffect(() => {
    if (!taskId) return;
    let cancelled = false;
    getTask(taskId)
      .then((t) => {
        if (cancelled) return;
        const directiveId = (t as Task & { directiveId?: string | null })
          .directiveId ?? null;
        // If this task actually IS attached to a directive, redirect
        // there — /tmp/ is reserved for genuine orphans.
        if (directiveId) {
          navigate(`/directives/${directiveId}?task=${taskId}`, {
            replace: true,
          });
          return;
        }
        setTask(t);
      })
      .catch((e) => {
        if (!cancelled) setError(e instanceof Error ? e.message : String(e));
      });
    return () => {
      cancelled = true;
    };
  }, [taskId, navigate]);

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

  return (
    <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
      <Masthead showNav />
      <main
        className="flex-1 flex flex-col overflow-hidden"
        style={{ height: "calc(100vh - 80px)" }}
      >
        {/* Breadcrumb echoing the document-mode header style. */}
        <div className="px-6 py-3 border-b border-dashed border-[rgba(117,170,252,0.2)]">
          <div className="flex items-center gap-2 text-[10px] font-mono uppercase tracking-wide text-[#7788aa]">
            <span>tmp /</span>
            <span className="text-[#9bc3ff]">
              {taskId ? taskId.slice(0, 8) : ""}
            </span>
            {task && <span className="normal-case text-[#dbe7ff]">— {task.name}</span>}
          </div>
        </div>

        {error ? (
          <div className="flex-1 flex items-center justify-center">
            <p className="text-red-400 font-mono text-xs">{error}</p>
          </div>
        ) : taskId ? (
          <DocumentTaskStream taskId={taskId} label={task?.name ?? taskId.slice(0, 8)} />
        ) : null}
      </main>
    </div>
  );
}