summaryrefslogtreecommitdiff
path: root/makima/frontend/src/routes/tmp.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/routes/tmp.tsx')
-rw-r--r--makima/frontend/src/routes/tmp.tsx93
1 files changed, 93 insertions, 0 deletions
diff --git a/makima/frontend/src/routes/tmp.tsx b/makima/frontend/src/routes/tmp.tsx
new file mode 100644
index 0000000..69f13a2
--- /dev/null
+++ b/makima/frontend/src/routes/tmp.tsx
@@ -0,0 +1,93 @@
+/**
+ * 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>
+ );
+}