From 9bca2d790fd103c7cf8069ec5241da0fd5f13915 Mon Sep 17 00:00:00 2001 From: soryu Date: Sat, 14 Feb 2026 02:34:42 +0000 Subject: feat: soryu-co/soryu - makima: Build Orders frontend page replacing the Board page --- .../src/components/workflow/PhaseColumn.tsx | 126 ----------- .../src/components/workflow/WorkflowBoard.tsx | 98 -------- .../components/workflow/WorkflowContractCard.tsx | 76 ------- makima/frontend/src/main.tsx | 12 +- makima/frontend/src/routes/orders.tsx | 4 +- makima/frontend/src/routes/workflow.tsx | 250 --------------------- makima/frontend/tsconfig.tsbuildinfo | 2 +- 7 files changed, 13 insertions(+), 555 deletions(-) delete mode 100644 makima/frontend/src/components/workflow/PhaseColumn.tsx delete mode 100644 makima/frontend/src/components/workflow/WorkflowBoard.tsx delete mode 100644 makima/frontend/src/components/workflow/WorkflowContractCard.tsx delete mode 100644 makima/frontend/src/routes/workflow.tsx 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 ( -
- {/* Column header */} -
- - {config.label} - - - ({contracts.length}) - -
- - {/* Cards container */} -
- {contracts.length === 0 ? ( -
- No contracts -
- ) : ( - contracts.map((contract) => ( - onContractClick(contract.id)} - onDragStart={(e) => { - e.dataTransfer.setData("contractId", contract.id); - e.dataTransfer.effectAllowed = "move"; - }} - onContextMenu={onContextMenu ? (e) => onContextMenu(e, contract) : undefined} - /> - )) - )} -
-
- ); -} 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(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 = { - 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 ( - <> -
- {phases.map((phase) => ( - - ))} -
- - {/* Context Menu */} - {contextMenuPosition && contextMenuContract && ( - 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 = { - 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 ( -
- {/* Header row with name and supervisor button */} -
-
- {contract.name} -
- {contract.supervisorTaskId && ( - - )} -
- - {/* Status and counts row */} -
- - {status.label} - -
- {contract.fileCount} files - {contract.taskCount} tasks -
-
- - {/* Description preview if exists */} - {contract.description && ( -
- {contract.description} -
- )} -
- ); -} diff --git a/makima/frontend/src/main.tsx b/makima/frontend/src/main.tsx index 1134bd3..acc9afc 100644 --- a/makima/frontend/src/main.tsx +++ b/makima/frontend/src/main.tsx @@ -81,10 +81,18 @@ createRoot(document.getElementById("root")!).render( } /> - + + + } + /> + + } /> diff --git a/makima/frontend/src/routes/orders.tsx b/makima/frontend/src/routes/orders.tsx index 456e1a7..735c557 100644 --- a/makima/frontend/src/routes/orders.tsx +++ b/makima/frontend/src/routes/orders.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect } from "react"; import { useParams, useNavigate } from "react-router"; import { Masthead } from "../components/Masthead"; import { OrderList } from "../components/orders/OrderList"; @@ -15,7 +15,7 @@ export default function OrdersPage() { const [statusFilter, setStatusFilter] = useState(undefined); const [typeFilter, setTypeFilter] = useState(undefined); - const { orders, loading: listLoading, create, remove: removeFromList, refresh: refreshList } = useOrders(statusFilter, typeFilter); + const { orders, loading: listLoading, create, refresh: refreshList } = useOrders(statusFilter, typeFilter); const { order, refresh: refreshDetail, update, remove: removeOrder, linkDirective, linkContract, convertToStep } = useOrder(selectedId); const { directives } = useDirectives(); 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 ( -
- -
-

Loading...

-
-
- ); - } - - // Don't render if not authenticated (will redirect) - if (isAuthConfigured && !isAuthenticated) { - return null; - } - - return ; -} - -function WorkflowPageContent() { - const navigate = useNavigate(); - const { contracts, loading, error, changePhase, saveContract, editContract, removeContract } = useContracts(); - const [statusFilter, setStatusFilter] = useState("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 ( -
- -
- {error && ( -
- {error} -
- )} - - {/* Header with filter and create button */} -
-
-

- Board -

- {/* Status filter */} -
- {(["all", "active", "completed", "archived"] as StatusFilter[]).map( - (status) => ( - - ) - )} -
-
- -
- - {/* Create contract modal */} - {isCreating && ( -
-
-

- Create Contract -

-
- 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(); - }} - /> -
- - -
-
-
-
- )} - - {/* Board */} -
- {loading ? ( -
-

Loading...

-
- ) : filteredContracts.length === 0 && statusFilter === "all" ? ( -
-
-

- No contracts yet -

- -
-
- ) : ( - - )} -
-
-
- ); -} diff --git a/makima/frontend/tsconfig.tsbuildinfo b/makima/frontend/tsconfig.tsbuildinfo index c2bf573..68f2eac 100644 --- a/makima/frontend/tsconfig.tsbuildinfo +++ b/makima/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/directives/directivedag.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/directivelogstream.tsx","./src/components/directives/stepnode.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectives.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usemultitasksubscription.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/directives.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/workflow.tsx","./src/types/messages.ts"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/directives/directivedag.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/directivelogstream.tsx","./src/components/directives/stepnode.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/orders/orderdetail.tsx","./src/components/orders/orderlist.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectives.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usemultitasksubscription.ts","./src/hooks/useorders.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/directives.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/orders.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/types/messages.ts"],"version":"5.9.3"} \ No newline at end of file -- cgit v1.2.3