summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--makima/frontend/src/components/NavStrip.tsx2
-rw-r--r--makima/frontend/src/components/workflow/PhaseColumn.tsx126
-rw-r--r--makima/frontend/src/components/workflow/WorkflowBoard.tsx98
-rw-r--r--makima/frontend/src/components/workflow/WorkflowContractCard.tsx76
-rw-r--r--makima/frontend/src/main.tsx9
-rw-r--r--makima/frontend/src/routes/workflow.tsx250
6 files changed, 1 insertions, 560 deletions
diff --git a/makima/frontend/src/components/NavStrip.tsx b/makima/frontend/src/components/NavStrip.tsx
index 9bb7777..5aba6a3 100644
--- a/makima/frontend/src/components/NavStrip.tsx
+++ b/makima/frontend/src/components/NavStrip.tsx
@@ -12,7 +12,7 @@ const NAV_LINKS: NavLink[] = [
{ label: "Listen", href: "/listen" },
{ label: "Directives", href: "/directives", requiresAuth: true },
{ label: "Contracts", href: "/contracts", requiresAuth: true },
- { label: "Board", href: "/workflow", requiresAuth: true },
+ { label: "Orders", href: "/orders", requiresAuth: true },
{ label: "Mesh", href: "/mesh", requiresAuth: true },
{ label: "History", href: "/history", requiresAuth: true },
];
diff --git a/makima/frontend/src/components/workflow/PhaseColumn.tsx b/makima/frontend/src/components/workflow/PhaseColumn.tsx
deleted file mode 100644
index 277b04c..0000000
--- a/makima/frontend/src/components/workflow/PhaseColumn.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-import { useState } from "react";
-import type { ContractSummary, ContractPhase } from "../../lib/api";
-import { WorkflowContractCard } from "./WorkflowContractCard";
-
-interface PhaseColumnProps {
- phase: ContractPhase;
- contracts: ContractSummary[];
- onContractClick: (contractId: string) => void;
- onDrop: (contractId: string, phase: ContractPhase) => void;
- onContextMenu?: (e: React.MouseEvent, contract: ContractSummary) => void;
-}
-
-const phaseConfig: Record<
- ContractPhase,
- { label: string; color: string; bgColor: string; borderColor: string }
-> = {
- research: {
- label: "Research",
- color: "text-purple-400",
- bgColor: "bg-purple-400/10",
- borderColor: "border-purple-400/30",
- },
- specify: {
- label: "Specify",
- color: "text-blue-400",
- bgColor: "bg-blue-400/10",
- borderColor: "border-blue-400/30",
- },
- plan: {
- label: "Plan",
- color: "text-cyan-400",
- bgColor: "bg-cyan-400/10",
- borderColor: "border-cyan-400/30",
- },
- execute: {
- label: "Execute",
- color: "text-yellow-400",
- bgColor: "bg-yellow-400/10",
- borderColor: "border-yellow-400/30",
- },
- review: {
- label: "Review",
- color: "text-green-400",
- bgColor: "bg-green-400/10",
- borderColor: "border-green-400/30",
- },
-};
-
-export function PhaseColumn({
- phase,
- contracts,
- onContractClick,
- onDrop,
- onContextMenu,
-}: PhaseColumnProps) {
- const [isDragOver, setIsDragOver] = useState(false);
- const config = phaseConfig[phase];
-
- const handleDragOver = (e: React.DragEvent) => {
- e.preventDefault();
- setIsDragOver(true);
- };
-
- const handleDragLeave = () => {
- setIsDragOver(false);
- };
-
- const handleDrop = (e: React.DragEvent) => {
- e.preventDefault();
- setIsDragOver(false);
- const contractId = e.dataTransfer.getData("contractId");
- if (contractId) {
- onDrop(contractId, phase);
- }
- };
-
- return (
- <div
- className={`
- flex flex-col min-w-[220px] flex-1 border border-[rgba(117,170,252,0.15)]
- ${isDragOver ? "bg-[rgba(117,170,252,0.05)]" : "bg-transparent"}
- transition-colors
- `}
- onDragOver={handleDragOver}
- onDragLeave={handleDragLeave}
- onDrop={handleDrop}
- >
- {/* Column header */}
- <div
- className={`
- p-3 border-b ${config.borderColor} ${config.bgColor}
- flex items-center justify-between
- `}
- >
- <span className={`font-mono text-xs uppercase tracking-wider ${config.color}`}>
- {config.label}
- </span>
- <span className="font-mono text-[10px] text-[#555]">
- ({contracts.length})
- </span>
- </div>
-
- {/* Cards container */}
- <div className="flex-1 overflow-y-auto p-2 space-y-2">
- {contracts.length === 0 ? (
- <div className="p-4 text-center font-mono text-[10px] text-[#555]">
- No contracts
- </div>
- ) : (
- contracts.map((contract) => (
- <WorkflowContractCard
- key={contract.id}
- contract={contract}
- onClick={() => onContractClick(contract.id)}
- onDragStart={(e) => {
- e.dataTransfer.setData("contractId", contract.id);
- e.dataTransfer.effectAllowed = "move";
- }}
- onContextMenu={onContextMenu ? (e) => onContextMenu(e, contract) : undefined}
- />
- ))
- )}
- </div>
- </div>
- );
-}
diff --git a/makima/frontend/src/components/workflow/WorkflowBoard.tsx b/makima/frontend/src/components/workflow/WorkflowBoard.tsx
deleted file mode 100644
index e36ca21..0000000
--- a/makima/frontend/src/components/workflow/WorkflowBoard.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { useMemo, useState } from "react";
-import type { ContractSummary, ContractPhase } from "../../lib/api";
-import { PhaseColumn } from "./PhaseColumn";
-import { ContractContextMenu } from "../contracts/ContractContextMenu";
-
-interface WorkflowBoardProps {
- contracts: ContractSummary[];
- onContractClick: (contractId: string) => void;
- onPhaseChange: (contractId: string, newPhase: ContractPhase) => void;
- onMarkComplete?: (contract: ContractSummary) => void;
- onMarkActive?: (contract: ContractSummary) => void;
- onArchive?: (contract: ContractSummary) => void;
- onDelete?: (contract: ContractSummary) => void;
- onGoToSupervisor?: (contract: ContractSummary) => void;
-}
-
-const phases: ContractPhase[] = ["research", "specify", "plan", "execute", "review"];
-
-export function WorkflowBoard({
- contracts,
- onContractClick,
- onPhaseChange,
- onMarkComplete,
- onMarkActive,
- onArchive,
- onDelete,
- onGoToSupervisor,
-}: WorkflowBoardProps) {
- const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null);
- const [contextMenuContract, setContextMenuContract] = useState<ContractSummary | null>(null);
-
- const handleContextMenu = (e: React.MouseEvent, contract: ContractSummary) => {
- e.preventDefault();
- e.stopPropagation(); // Prevent interference with drag-and-drop
- setContextMenuPosition({ x: e.clientX, y: e.clientY });
- setContextMenuContract(contract);
- };
-
- const closeContextMenu = () => {
- setContextMenuPosition(null);
- setContextMenuContract(null);
- };
-
- // Group contracts by phase
- const contractsByPhase = useMemo(() => {
- const grouped: Record<ContractPhase, ContractSummary[]> = {
- research: [],
- specify: [],
- plan: [],
- execute: [],
- review: [],
- };
-
- for (const contract of contracts) {
- const phase = contract.phase as ContractPhase;
- if (grouped[phase]) {
- grouped[phase].push(contract);
- } else {
- // Default to research if unknown phase
- grouped.research.push(contract);
- }
- }
-
- return grouped;
- }, [contracts]);
-
- return (
- <>
- <div className="flex gap-2 h-full overflow-x-auto">
- {phases.map((phase) => (
- <PhaseColumn
- key={phase}
- phase={phase}
- contracts={contractsByPhase[phase]}
- onContractClick={onContractClick}
- onDrop={onPhaseChange}
- onContextMenu={handleContextMenu}
- />
- ))}
- </div>
-
- {/* Context Menu */}
- {contextMenuPosition && contextMenuContract && (
- <ContractContextMenu
- x={contextMenuPosition.x}
- y={contextMenuPosition.y}
- contract={contextMenuContract}
- onClose={closeContextMenu}
- onMarkComplete={() => onMarkComplete?.(contextMenuContract)}
- onMarkActive={() => onMarkActive?.(contextMenuContract)}
- onArchive={() => onArchive?.(contextMenuContract)}
- onDelete={() => onDelete?.(contextMenuContract)}
- onGoToSupervisor={() => onGoToSupervisor?.(contextMenuContract)}
- />
- )}
- </>
- );
-}
diff --git a/makima/frontend/src/components/workflow/WorkflowContractCard.tsx b/makima/frontend/src/components/workflow/WorkflowContractCard.tsx
deleted file mode 100644
index 86fcd13..0000000
--- a/makima/frontend/src/components/workflow/WorkflowContractCard.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { useNavigate } from "react-router";
-import type { ContractSummary, ContractStatus } from "../../lib/api";
-
-interface WorkflowContractCardProps {
- contract: ContractSummary;
- onClick: () => void;
- onDragStart: (e: React.DragEvent) => void;
- onContextMenu?: (e: React.MouseEvent) => void;
-}
-
-const statusConfig: Record<ContractStatus, { label: string; color: string }> = {
- active: { label: "Active", color: "text-green-400" },
- completed: { label: "Done", color: "text-blue-400" },
- archived: { label: "Archived", color: "text-[#555]" },
-};
-
-export function WorkflowContractCard({
- contract,
- onClick,
- onDragStart,
- onContextMenu,
-}: WorkflowContractCardProps) {
- const navigate = useNavigate();
- const status = statusConfig[contract.status] || statusConfig.active;
-
- const handleSupervisorClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- if (contract.supervisorTaskId) {
- navigate(`/mesh/${contract.supervisorTaskId}`);
- }
- };
-
- return (
- <div
- draggable
- onDragStart={onDragStart}
- onClick={onClick}
- onContextMenu={onContextMenu}
- className="p-3 bg-[rgba(9,13,20,0.8)] border border-[rgba(117,170,252,0.2)] hover:border-[rgba(117,170,252,0.4)] cursor-pointer transition-colors select-none"
- >
- {/* Header row with name and supervisor button */}
- <div className="flex items-center justify-between gap-2 mb-1">
- <div className="font-mono text-sm text-[#dbe7ff] truncate flex-1">
- {contract.name}
- </div>
- {contract.supervisorTaskId && (
- <button
- onClick={handleSupervisorClick}
- title="Open Supervisor Task"
- className="flex-shrink-0 px-1.5 py-0.5 font-mono text-[10px] text-[#75aafc] hover:text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] hover:bg-[rgba(117,170,252,0.1)] transition-colors"
- >
- ▶
- </button>
- )}
- </div>
-
- {/* Status and counts row */}
- <div className="flex items-center justify-between">
- <span className={`font-mono text-[10px] uppercase ${status.color}`}>
- {status.label}
- </span>
- <div className="flex items-center gap-2 font-mono text-[10px] text-[#555]">
- <span title="Files">{contract.fileCount} files</span>
- <span title="Tasks">{contract.taskCount} tasks</span>
- </div>
- </div>
-
- {/* Description preview if exists */}
- {contract.description && (
- <div className="mt-1 font-mono text-[10px] text-[#555] truncate">
- {contract.description}
- </div>
- )}
- </div>
- );
-}
diff --git a/makima/frontend/src/main.tsx b/makima/frontend/src/main.tsx
index 3dc68f5..7698687 100644
--- a/makima/frontend/src/main.tsx
+++ b/makima/frontend/src/main.tsx
@@ -12,7 +12,6 @@ import HomePage from "./routes/_index";
import ListenPage from "./routes/listen";
import FilesPage from "./routes/files";
import ContractsPage from "./routes/contracts";
-import WorkflowPage from "./routes/workflow";
import MeshPage from "./routes/mesh";
import HistoryPage from "./routes/history";
import LoginPage from "./routes/login";
@@ -81,14 +80,6 @@ createRoot(document.getElementById("root")!).render(
}
/>
<Route
- path="/workflow"
- element={
- <ProtectedRoute>
- <WorkflowPage />
- </ProtectedRoute>
- }
- />
- <Route
path="/mesh"
element={
<ProtectedRoute>
diff --git a/makima/frontend/src/routes/workflow.tsx b/makima/frontend/src/routes/workflow.tsx
deleted file mode 100644
index e122092..0000000
--- a/makima/frontend/src/routes/workflow.tsx
+++ /dev/null
@@ -1,250 +0,0 @@
-import { useState, useCallback, useEffect, useMemo } from "react";
-import { useNavigate } from "react-router";
-import { Masthead } from "../components/Masthead";
-import { WorkflowBoard } from "../components/workflow/WorkflowBoard";
-import { useContracts } from "../hooks/useContracts";
-import { useAuth } from "../contexts/AuthContext";
-import type { ContractPhase, ContractStatus, ContractSummary } from "../lib/api";
-
-type StatusFilter = "all" | ContractStatus;
-
-export default function WorkflowPage() {
- 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 <WorkflowPageContent />;
-}
-
-function WorkflowPageContent() {
- const navigate = useNavigate();
- const { contracts, loading, error, changePhase, saveContract, editContract, removeContract } = useContracts();
- const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
- const [isCreating, setIsCreating] = useState(false);
- const [newContractName, setNewContractName] = useState("");
-
- // Filter contracts by status
- const filteredContracts = useMemo(() => {
- if (statusFilter === "all") {
- return contracts;
- }
- return contracts.filter((c) => c.status === statusFilter);
- }, [contracts, statusFilter]);
-
- const handleContractClick = useCallback(
- (contractId: string) => {
- navigate(`/contracts/${contractId}`);
- },
- [navigate]
- );
-
- const handlePhaseChange = useCallback(
- async (contractId: string, newPhase: ContractPhase) => {
- await changePhase(contractId, newPhase);
- },
- [changePhase]
- );
-
- // Context menu handlers
- const handleContextMarkComplete = useCallback(
- async (contract: ContractSummary) => {
- await editContract(contract.id, { status: "completed", version: contract.version });
- },
- [editContract]
- );
-
- const handleContextMarkActive = useCallback(
- async (contract: ContractSummary) => {
- await editContract(contract.id, { status: "active", version: contract.version });
- },
- [editContract]
- );
-
- const handleContextArchive = useCallback(
- async (contract: ContractSummary) => {
- await editContract(contract.id, { status: "archived", version: contract.version });
- },
- [editContract]
- );
-
- const handleContextDelete = useCallback(
- async (contract: ContractSummary) => {
- if (confirm(`Are you sure you want to delete "${contract.name}"?`)) {
- await removeContract(contract.id);
- }
- },
- [removeContract]
- );
-
- const handleContextGoToSupervisor = useCallback(
- (contract: ContractSummary) => {
- if (contract.supervisorTaskId) {
- navigate(`/mesh/${contract.supervisorTaskId}`);
- }
- },
- [navigate]
- );
-
- const handleCreateContract = useCallback(async () => {
- if (!newContractName.trim()) return;
- const contract = await saveContract({
- name: newContractName.trim(),
- });
- if (contract) {
- setNewContractName("");
- setIsCreating(false);
- navigate(`/contracts/${contract.id}`);
- }
- }, [newContractName, saveContract, navigate]);
-
- const handleCancelCreate = useCallback(() => {
- setNewContractName("");
- setIsCreating(false);
- }, []);
-
- return (
- <div className="relative z-10 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 shrink-0">
- {error}
- </div>
- )}
-
- {/* Header with filter and create button */}
- <div className="flex items-center justify-between shrink-0">
- <div className="flex items-center gap-4">
- <h1 className="font-mono text-sm text-[#75aafc] uppercase tracking-wider">
- Board
- </h1>
- {/* Status filter */}
- <div className="flex items-center gap-1">
- {(["all", "active", "completed", "archived"] as StatusFilter[]).map(
- (status) => (
- <button
- key={status}
- onClick={() => setStatusFilter(status)}
- className={`
- px-2 py-1 font-mono text-[10px] uppercase transition-colors
- ${
- statusFilter === status
- ? "bg-[rgba(117,170,252,0.1)] text-[#9bc3ff] border border-[rgba(117,170,252,0.3)]"
- : "text-[#555] border border-transparent hover:text-[#75aafc]"
- }
- `}
- >
- {status}
- </button>
- )
- )}
- </div>
- </div>
- <button
- onClick={() => setIsCreating(true)}
- className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors"
- >
- + New Contract
- </button>
- </div>
-
- {/* Create contract modal */}
- {isCreating && (
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
- <div className="w-full max-w-md p-6 bg-[#0a1628] border border-[rgba(117,170,252,0.3)]">
- <h3 className="font-mono text-sm text-[#75aafc] uppercase mb-4">
- Create Contract
- </h3>
- <div className="space-y-4">
- <input
- type="text"
- value={newContractName}
- onChange={(e) => setNewContractName(e.target.value)}
- placeholder="Contract name"
- 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
- onKeyDown={(e) => {
- if (e.key === "Enter") handleCreateContract();
- if (e.key === "Escape") handleCancelCreate();
- }}
- />
- <div className="flex gap-2 justify-end">
- <button
- onClick={handleCancelCreate}
- className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
- >
- Cancel
- </button>
- <button
- onClick={handleCreateContract}
- disabled={!newContractName.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>
- )}
-
- {/* Board */}
- <div className="flex-1 min-h-0 overflow-hidden">
- {loading ? (
- <div className="h-full flex items-center justify-center">
- <p className="font-mono text-sm text-[#555]">Loading...</p>
- </div>
- ) : filteredContracts.length === 0 && statusFilter === "all" ? (
- <div className="h-full flex items-center justify-center">
- <div className="text-center">
- <p className="font-mono text-sm text-[#555] mb-4">
- No contracts yet
- </p>
- <button
- onClick={() => setIsCreating(true)}
- className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
- >
- + Create First Contract
- </button>
- </div>
- </div>
- ) : (
- <WorkflowBoard
- contracts={filteredContracts}
- onContractClick={handleContractClick}
- onPhaseChange={handlePhaseChange}
- onMarkComplete={handleContextMarkComplete}
- onMarkActive={handleContextMarkActive}
- onArchive={handleContextArchive}
- onDelete={handleContextDelete}
- onGoToSupervisor={handleContextGoToSupervisor}
- />
- )}
- </div>
- </main>
- </div>
- );
-}