summaryrefslogtreecommitdiff
path: root/makima/frontend/src/routes/workflow.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/routes/workflow.tsx')
-rw-r--r--makima/frontend/src/routes/workflow.tsx250
1 files changed, 0 insertions, 250 deletions
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>
- );
-}