diff options
| author | soryu <soryu@soryu.co> | 2026-03-05 23:25:40 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-03-07 02:28:19 +0000 |
| commit | ae3bc57de7a240c3c8ab15080b405e8ea3e16ccb (patch) | |
| tree | 96562ad1b73efa0f21ea79ae571e1c8674549d31 | |
| parent | b8ec238084d9d5b210a67bc8c8cbb9a293facf28 (diff) | |
| download | soryu-makima/directive-soryu-co-soryu---makima-19fd3e1d-v1772803139.tar.gz soryu-makima/directive-soryu-co-soryu---makima-19fd3e1d-v1772803139.zip | |
WIP: heartbeat checkpointmakima/directive-soryu-co-soryu---makima-19fd3e1d-v1772803139
| -rw-r--r-- | makima/frontend/src/components/directives/DOGList.tsx | 381 | ||||
| -rw-r--r-- | makima/frontend/src/components/directives/DirectiveDetail.tsx | 72 | ||||
| -rw-r--r-- | makima/frontend/src/components/orders/OrderDetail.tsx | 91 | ||||
| -rw-r--r-- | makima/frontend/src/routes/directives.tsx | 8 | ||||
| -rw-r--r-- | makima/frontend/src/routes/orders.tsx | 3 |
5 files changed, 542 insertions, 13 deletions
diff --git a/makima/frontend/src/components/directives/DOGList.tsx b/makima/frontend/src/components/directives/DOGList.tsx new file mode 100644 index 0000000..de59d7d --- /dev/null +++ b/makima/frontend/src/components/directives/DOGList.tsx @@ -0,0 +1,381 @@ +import { useState } from "react"; +import type { + DirectiveOrderGroup, + DOGStatus, + CreateDOGRequest, +} from "../../lib/api"; + +const DOG_STATUS_BADGE: Record<DOGStatus, { color: string; label: string }> = { + open: { color: "text-[#75aafc] border-[rgba(117,170,252,0.4)]", label: "OPEN" }, + in_progress: { color: "text-yellow-400 border-yellow-800", label: "IN PROGRESS" }, + done: { color: "text-emerald-400 border-emerald-800", label: "DONE" }, + archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" }, +}; + +const DOG_STATUS_OPTIONS: DOGStatus[] = ["open", "in_progress", "done", "archived"]; + +interface DOGListProps { + dogs: DirectiveOrderGroup[]; + loading: boolean; + onCreateDog: (req: CreateDOGRequest) => Promise<DirectiveOrderGroup | null>; + onUpdateDog: (dogId: string, req: { name?: string; description?: string | null; status?: DOGStatus }) => Promise<void>; + onDeleteDog: (dogId: string) => Promise<void>; + onPickUpOrders: (dogId: string) => Promise<any>; +} + +export function DOGList({ + dogs, + loading, + onCreateDog, + onUpdateDog, + onDeleteDog, + onPickUpOrders, +}: DOGListProps) { + const [showCreate, setShowCreate] = useState(false); + const [newName, setNewName] = useState(""); + const [newDesc, setNewDesc] = useState(""); + const [creating, setCreating] = useState(false); + + const handleCreate = async () => { + if (!newName.trim()) return; + setCreating(true); + try { + await onCreateDog({ + name: newName.trim(), + description: newDesc.trim() || null, + }); + setNewName(""); + setNewDesc(""); + setShowCreate(false); + } catch (e) { + console.error("Failed to create DOG:", e); + } finally { + setCreating(false); + } + }; + + if (loading) { + return ( + <div className="flex items-center justify-center py-8"> + <span className="text-[10px] font-mono text-[#556677]">Loading DOGs...</span> + </div> + ); + } + + return ( + <div className="flex flex-col gap-3"> + {/* Header + Create button */} + <div className="flex items-center justify-between"> + <span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide"> + Order Groups ({dogs.length}) + </span> + <button + type="button" + onClick={() => setShowCreate(!showCreate)} + className="text-[10px] font-mono text-[#75aafc] hover:text-white border border-[rgba(117,170,252,0.3)] rounded px-2 py-0.5" + > + {showCreate ? "Cancel" : "+ New DOG"} + </button> + </div> + + {/* Create form */} + {showCreate && ( + <div className="border border-[rgba(117,170,252,0.2)] bg-[#0a1525] rounded p-3 flex flex-col gap-2"> + <div> + <label className="text-[9px] font-mono text-[#7788aa] uppercase tracking-wide block mb-1"> + Name * + </label> + <input + value={newName} + onChange={(e) => setNewName(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter" && newName.trim()) handleCreate(); + if (e.key === "Escape") setShowCreate(false); + }} + placeholder="Group name..." + autoFocus + className="w-full bg-[#0a1628] border border-[rgba(117,170,252,0.2)] rounded px-2 py-1.5 text-[11px] font-mono text-white placeholder:text-[#445566]" + /> + </div> + <div> + <label className="text-[9px] font-mono text-[#7788aa] uppercase tracking-wide block mb-1"> + Description + </label> + <textarea + value={newDesc} + onChange={(e) => setNewDesc(e.target.value)} + placeholder="Optional description..." + rows={2} + className="w-full bg-[#0a1628] border border-[rgba(117,170,252,0.2)] rounded px-2 py-1.5 text-[11px] font-mono text-white resize-y placeholder:text-[#445566]" + /> + </div> + <div className="flex gap-1.5"> + <button + type="button" + onClick={handleCreate} + disabled={!newName.trim() || creating} + className="text-[10px] font-mono text-emerald-400 hover:text-emerald-300 border border-emerald-800 rounded px-2 py-0.5 disabled:opacity-50" + > + {creating ? "Creating..." : "Create"} + </button> + <button + type="button" + onClick={() => setShowCreate(false)} + className="text-[10px] font-mono text-[#7788aa] hover:text-white border border-[#2a3a5a] rounded px-2 py-0.5" + > + Cancel + </button> + </div> + </div> + )} + + {/* DOG list */} + {dogs.length === 0 ? ( + <div className="flex items-center justify-center py-6"> + <span className="text-[10px] font-mono text-[#556677] italic"> + No order groups yet + </span> + </div> + ) : ( + <div className="flex flex-col gap-2"> + {dogs.map((dog) => ( + <DOGCard + key={dog.id} + dog={dog} + onUpdate={onUpdateDog} + onDelete={onDeleteDog} + onPickUpOrders={onPickUpOrders} + /> + ))} + </div> + )} + </div> + ); +} + +function DOGCard({ + dog, + onUpdate, + onDelete, + onPickUpOrders, +}: { + dog: DirectiveOrderGroup; + onUpdate: (dogId: string, req: { name?: string; description?: string | null; status?: DOGStatus }) => Promise<void>; + onDelete: (dogId: string) => Promise<void>; + onPickUpOrders: (dogId: string) => Promise<any>; +}) { + const [editingName, setEditingName] = useState(false); + const [nameText, setNameText] = useState(dog.name); + const [editingDesc, setEditingDesc] = useState(false); + const [descText, setDescText] = useState(dog.description || ""); + const [pickingUp, setPickingUp] = useState(false); + const [pickUpResult, setPickUpResult] = useState<string | null>(null); + const [deleting, setDeleting] = useState(false); + + const badge = DOG_STATUS_BADGE[dog.status] || DOG_STATUS_BADGE.open; + + const handleNameSave = async () => { + if (nameText.trim() && nameText !== dog.name) { + await onUpdate(dog.id, { name: nameText.trim() }); + } + setEditingName(false); + }; + + const handleDescSave = async () => { + const newDesc = descText.trim() || null; + if (newDesc !== dog.description) { + await onUpdate(dog.id, { description: newDesc }); + } + setEditingDesc(false); + }; + + const handleStatusChange = async (status: DOGStatus) => { + await onUpdate(dog.id, { status }); + }; + + const handlePickUpOrders = async () => { + setPickingUp(true); + setPickUpResult(null); + try { + const result = await onPickUpOrders(dog.id); + if (result) { + setPickUpResult(result.message); + setTimeout(() => setPickUpResult(null), 5000); + } + } catch (e) { + setPickUpResult(e instanceof Error ? e.message : "Failed to plan orders"); + setTimeout(() => setPickUpResult(null), 5000); + } finally { + setPickingUp(false); + } + }; + + const handleDelete = async () => { + if (!window.confirm(`Delete DOG "${dog.name}"?`)) return; + setDeleting(true); + try { + await onDelete(dog.id); + } catch (e) { + console.error("Failed to delete DOG:", e); + setDeleting(false); + } + }; + + return ( + <div className="border border-[rgba(117,170,252,0.15)] bg-[#0a1525] rounded"> + {/* Name + Status */} + <div className="px-3 py-2 border-b border-[rgba(117,170,252,0.08)]"> + <div className="flex items-center justify-between mb-1.5"> + {editingName ? ( + <div className="flex-1 flex items-center gap-2 pr-2"> + <input + value={nameText} + onChange={(e) => setNameText(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") handleNameSave(); + if (e.key === "Escape") setEditingName(false); + }} + autoFocus + className="flex-1 bg-[#0a1628] border border-[rgba(117,170,252,0.2)] rounded px-2 py-0.5 text-[12px] font-mono text-white" + /> + <button + type="button" + onClick={handleNameSave} + className="text-[9px] font-mono text-emerald-400 hover:text-emerald-300" + > + [save] + </button> + <button + type="button" + onClick={() => setEditingName(false)} + className="text-[9px] font-mono text-[#556677] hover:text-white" + > + [cancel] + </button> + </div> + ) : ( + <span + className="text-[12px] font-mono text-white font-medium cursor-pointer hover:text-[#9bc3ff] truncate pr-2" + onClick={() => { + setNameText(dog.name); + setEditingName(true); + }} + > + {dog.name} + </span> + )} + <span className={`text-[9px] font-mono ${badge.color} border rounded px-1.5 py-0.5 shrink-0`}> + {badge.label} + </span> + </div> + + {/* Status selector */} + <div className="flex gap-1 flex-wrap"> + {DOG_STATUS_OPTIONS.map((s) => { + const sBadge = DOG_STATUS_BADGE[s]; + return ( + <button + key={s} + type="button" + onClick={() => handleStatusChange(s)} + className={`text-[9px] font-mono border rounded px-1.5 py-0.5 transition-colors ${ + s === dog.status + ? `${sBadge.color} bg-[rgba(117,170,252,0.1)]` + : "text-[#556677] border-[#2a3a5a] hover:text-[#7788aa]" + }`} + > + {sBadge.label} + </button> + ); + })} + </div> + </div> + + {/* Description */} + <div className="px-3 py-2 border-b border-[rgba(117,170,252,0.08)]"> + <div className="flex items-center justify-between mb-1"> + <span className="text-[9px] font-mono text-[#7788aa] uppercase tracking-wide"> + Description + </span> + {!editingDesc && ( + <button + type="button" + onClick={() => { + setDescText(dog.description || ""); + setEditingDesc(true); + }} + className="text-[8px] font-mono text-[#556677] hover:text-[#75aafc]" + > + [edit] + </button> + )} + </div> + {editingDesc ? ( + <div className="flex flex-col gap-1"> + <textarea + value={descText} + onChange={(e) => setDescText(e.target.value)} + className="w-full bg-[#0a1628] border border-[rgba(117,170,252,0.2)] rounded px-2 py-1 text-[10px] font-mono text-white resize-y min-h-[40px]" + rows={2} + autoFocus + /> + <div className="flex gap-1"> + <button + type="button" + onClick={handleDescSave} + className="text-[9px] font-mono text-emerald-400 hover:text-emerald-300 border border-emerald-800 rounded px-1.5 py-0.5" + > + Save + </button> + <button + type="button" + onClick={() => setEditingDesc(false)} + className="text-[9px] font-mono text-[#7788aa] hover:text-white border border-[#2a3a5a] rounded px-1.5 py-0.5" + > + Cancel + </button> + </div> + </div> + ) : ( + <p className="text-[10px] font-mono text-[#c0d0e0] whitespace-pre-wrap"> + {dog.description || <span className="text-[#556677] italic">No description</span>} + </p> + )} + </div> + + {/* Actions */} + <div className="px-3 py-2 flex items-center gap-2"> + <button + type="button" + onClick={handlePickUpOrders} + disabled={pickingUp} + className="text-[10px] font-mono text-[#c084fc] hover:text-[#d8b4fe] border border-[rgba(192,132,252,0.3)] rounded px-2 py-0.5 disabled:opacity-50" + > + {pickingUp ? "Planning..." : "Plan Orders"} + </button> + <button + type="button" + onClick={handleDelete} + disabled={deleting} + className="text-[10px] font-mono text-red-400 hover:text-red-300 border border-red-800 rounded px-2 py-0.5 ml-auto disabled:opacity-50" + > + {deleting ? "Deleting..." : "Delete"} + </button> + </div> + + {/* Pick-up result */} + {pickUpResult && ( + <div className="mx-3 mb-2 px-2 py-1.5 bg-[#1a1030] border border-[rgba(192,132,252,0.2)] rounded"> + <span className="text-[10px] font-mono text-[#c084fc]">{pickUpResult}</span> + </div> + )} + + {/* Metadata */} + <div className="px-3 py-1.5 border-t border-[rgba(117,170,252,0.05)]"> + <span className="text-[8px] font-mono text-[#445566]"> + Created {new Date(dog.createdAt).toLocaleDateString()} + </span> + </div> + </div> + ); +} diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx index 5f3489a..e3302e4 100644 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx @@ -1,9 +1,10 @@ import { useState, useMemo, useEffect, useRef } from "react"; -import type { DirectiveWithSteps, DirectiveStatus, UpdateDirectiveRequest } from "../../lib/api"; +import type { DirectiveWithSteps, DirectiveStatus, UpdateDirectiveRequest, DirectiveOrderGroup, CreateDOGRequest, UpdateDOGRequest } from "../../lib/api"; import { DirectiveDAG } from "./DirectiveDAG"; import type { SpecializedStep } from "./DirectiveDAG"; import { DirectiveLogStream } from "./DirectiveLogStream"; import { TaskSlideOutPanel } from "./TaskSlideOutPanel"; +import { DOGList } from "./DOGList"; import { useMultiTaskSubscription } from "../../hooks/useMultiTaskSubscription"; import { useSupervisorQuestions } from "../../contexts/SupervisorQuestionsContext"; @@ -30,6 +31,12 @@ interface DirectiveDetailProps { onCleanup: () => void; onPickUpOrders: () => Promise<{ message: string; orderCount: number; taskId: string | null } | null>; onCreatePR: () => Promise<void>; + dogs: DirectiveOrderGroup[]; + dogsLoading: boolean; + onCreateDog: (req: CreateDOGRequest) => Promise<DirectiveOrderGroup | null>; + onUpdateDog: (dogId: string, req: UpdateDOGRequest) => Promise<void>; + onDeleteDog: (dogId: string) => Promise<void>; + onPickUpDogOrders: (dogId: string) => Promise<any>; } export function DirectiveDetail({ @@ -47,7 +54,14 @@ export function DirectiveDetail({ onCleanup, onPickUpOrders, onCreatePR, + dogs, + dogsLoading, + onCreateDog, + onUpdateDog, + onDeleteDog, + onPickUpDogOrders, }: DirectiveDetailProps) { + const [activeTab, setActiveTab] = useState<"steps" | "dogs">("steps"); const [editingGoal, setEditingGoal] = useState(false); const [goalText, setGoalText] = useState(directive.goal); const [visibleTaskIds, setVisibleTaskIds] = useState<Set<string> | null>(null); @@ -437,21 +451,53 @@ export function DirectiveDetail({ )} </div> - {/* DAG */} - <div className="px-4 py-3 flex-1"> - <span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide block mb-2"> + {/* Tab bar */} + <div className="flex items-center gap-0 border-b border-[rgba(117,170,252,0.1)] px-4"> + <button + type="button" + onClick={() => setActiveTab("steps")} + className={`px-3 py-2 text-[10px] font-mono uppercase tracking-wide transition-colors + ${activeTab === "steps" ? "text-[#dbe7ff] border-b-2 border-[#75aafc]" : "text-[#556677] hover:text-[#9bc3ff]"} + `} + > Steps ({totalSteps}) - </span> - <DirectiveDAG - steps={directive.steps} - specializedSteps={specializedSteps} - onComplete={onCompleteStep} - onFail={onFailStep} - onSkip={onSkipStep} - onViewTask={handleViewTask} - /> + </button> + <button + type="button" + onClick={() => setActiveTab("dogs")} + className={`px-3 py-2 text-[10px] font-mono uppercase tracking-wide transition-colors + ${activeTab === "dogs" ? "text-[#dbe7ff] border-b-2 border-[#75aafc]" : "text-[#556677] hover:text-[#9bc3ff]"} + `} + > + DOGs ({dogs.length}) + </button> </div> + {/* Tab content */} + {activeTab === "steps" ? ( + <div className="px-4 py-3 flex-1"> + <DirectiveDAG + steps={directive.steps} + specializedSteps={specializedSteps} + onComplete={onCompleteStep} + onFail={onFailStep} + onSkip={onSkipStep} + onViewTask={handleViewTask} + /> + </div> + ) : ( + <div className="px-4 py-3 flex-1"> + <DOGList + dogs={dogs} + loading={dogsLoading} + onCreateDog={onCreateDog} + onUpdateDog={onUpdateDog} + onDeleteDog={onDeleteDog} + onPickUpOrders={onPickUpDogOrders} + /> + </div> + )} + {/* Log Stream */} {taskMap.size > 0 && ( <div className="px-4 py-3 border-t border-[rgba(117,170,252,0.1)]"> diff --git a/makima/frontend/src/components/orders/OrderDetail.tsx b/makima/frontend/src/components/orders/OrderDetail.tsx index 9c3ac97..4267725 100644 --- a/makima/frontend/src/components/orders/OrderDetail.tsx +++ b/makima/frontend/src/components/orders/OrderDetail.tsx @@ -6,6 +6,7 @@ import type { OrderType, UpdateOrderRequest, DirectiveSummary, + DirectiveOrderGroup, } from "../../lib/api"; const STATUS_BADGE: Record<OrderStatus, { color: string; label: string }> = { @@ -37,6 +38,7 @@ const STATUS_OPTIONS: OrderStatus[] = ["open", "in_progress", "under_review", "d interface OrderDetailProps { order: Order; directives: DirectiveSummary[]; + dogs: DirectiveOrderGroup[]; onUpdate: (req: UpdateOrderRequest) => Promise<void>; onDelete: () => void; onLinkDirective: (directiveId: string) => Promise<void>; @@ -47,6 +49,7 @@ interface OrderDetailProps { export function OrderDetail({ order, directives, + dogs, onUpdate, onDelete, onLinkDirective, @@ -61,6 +64,7 @@ export function OrderDetail({ const [labelsText, setLabelsText] = useState(order.labels.join(", ")); const [showLinkDirective, setShowLinkDirective] = useState(false); const [directiveSearch, setDirectiveSearch] = useState(""); + const [showDogSelector, setShowDogSelector] = useState(false); const badge = STATUS_BADGE[order.status] || STATUS_BADGE.open; const currentPriority = PRIORITY_OPTIONS.find((p) => p.value === order.priority) || PRIORITY_OPTIONS[4]; @@ -192,6 +196,18 @@ export function OrderDetail({ Step: <span className="text-[#7788aa]">{order.directiveStepId.slice(0, 8)}...</span> </div> )} + {order.directiveId && ( + <div className="text-[10px] font-mono text-[#556677] mb-1"> + DOG:{" "} + {order.dogId ? ( + <span className="text-[#75aafc]"> + {dogs.find((d) => d.id === order.dogId)?.name || order.dogId.slice(0, 8) + "..."} + </span> + ) : ( + <span className="text-[#445566] italic">None</span> + )} + </div> + )} {/* Controls */} <div className="flex flex-wrap gap-2 mt-2"> @@ -501,6 +517,81 @@ export function OrderDetail({ )} </div> + {/* Assign to DOG */} + {order.directiveId && ( + <div> + <div className="flex items-center gap-1.5"> + <button + type="button" + onClick={() => setShowDogSelector(!showDogSelector)} + className="text-[10px] font-mono text-[#75aafc] hover:text-white border border-[rgba(117,170,252,0.3)] rounded px-2 py-1 flex-1 text-left" + > + {order.dogId ? "Change DOG" : "Assign to DOG"} + </button> + {order.dogId && ( + <button + type="button" + onClick={() => onUpdate({ dogId: null })} + className="text-[10px] font-mono text-red-400 hover:text-red-300 border border-red-800 rounded px-2 py-1" + title="Remove DOG assignment" + > + Unlink + </button> + )} + </div> + {showDogSelector && ( + <div className="mt-1 border border-[rgba(117,170,252,0.2)] bg-[#0a1525] rounded"> + <div className="max-h-32 overflow-y-auto"> + {dogs.length === 0 ? ( + <div className="px-3 py-2 text-[10px] font-mono text-[#556677]"> + No DOGs available for this directive + </div> + ) : ( + dogs.map((d) => { + const isAssigned = d.id === order.dogId; + const statusColors: Record<string, string> = { + open: "text-[#75aafc] border-[rgba(117,170,252,0.4)]", + in_progress: "text-yellow-400 border-yellow-800", + done: "text-emerald-400 border-emerald-800", + archived: "text-[#556677] border-[#2a3a5a]", + }; + const sColor = statusColors[d.status] || statusColors.open; + return ( + <button + key={d.id} + type="button" + onClick={async () => { + await onUpdate({ dogId: d.id }); + setShowDogSelector(false); + }} + className={`w-full text-left px-3 py-1.5 text-[10px] font-mono hover:bg-[rgba(117,170,252,0.1)] border-b border-[rgba(117,170,252,0.1)] last:border-b-0 ${ + isAssigned ? "bg-[rgba(117,170,252,0.08)] text-white" : "text-[#9bc3ff]" + }`} + > + <div className="flex items-center gap-1.5"> + <span className={`shrink-0 text-[8px] font-mono ${sColor} border rounded px-1 py-0.5 uppercase`}> + {d.status} + </span> + <span className="truncate">{d.name}</span> + {isAssigned && ( + <span className="shrink-0 text-[8px] text-emerald-400">current</span> + )} + </div> + {d.description && ( + <div className="text-[8px] text-[#556677] truncate mt-0.5"> + {d.description} + </div> + )} + </button> + ); + }) + )} + </div> + </div> + )} + </div> + )} + {/* Convert to Directive Step */} {!order.directiveStepId && order.directiveId && ( <button diff --git a/makima/frontend/src/routes/directives.tsx b/makima/frontend/src/routes/directives.tsx index cee4920..846f52f 100644 --- a/makima/frontend/src/routes/directives.tsx +++ b/makima/frontend/src/routes/directives.tsx @@ -4,6 +4,7 @@ import { Masthead } from "../components/Masthead"; import { DirectiveList } from "../components/directives/DirectiveList"; import { DirectiveDetail } from "../components/directives/DirectiveDetail"; import { useDirectives, useDirective } from "../hooks/useDirectives"; +import { useDogs } from "../hooks/useDogs"; import { useAuth } from "../contexts/AuthContext"; import { getRepositorySuggestions, type RepositoryHistoryEntry } from "../lib/api"; @@ -13,6 +14,7 @@ export default function DirectivesPage() { const { id: selectedId } = useParams<{ id: string }>(); const { directives, loading: listLoading, create, remove } = useDirectives(); const { directive, refresh: refreshDetail, update, start, pause, advance, completeStep, failStep, skipStep, updateGoal, cleanup, pickUpOrders, createPR } = useDirective(selectedId); + const { dogs, loading: dogsLoading, create: createDog, update: updateDog, remove: removeDog, pickUpOrders: pickUpDogOrders } = useDogs(selectedId); const [showCreate, setShowCreate] = useState(false); const [newTitle, setNewTitle] = useState(""); @@ -213,6 +215,12 @@ export default function DirectivesPage() { onCleanup={cleanup} onPickUpOrders={pickUpOrders} onCreatePR={createPR} + dogs={dogs} + dogsLoading={dogsLoading} + onCreateDog={createDog} + onUpdateDog={updateDog} + onDeleteDog={removeDog} + onPickUpDogOrders={pickUpDogOrders} /> ) : ( <div className="flex-1 flex items-center justify-center h-full"> diff --git a/makima/frontend/src/routes/orders.tsx b/makima/frontend/src/routes/orders.tsx index aa14e68..06e091a 100644 --- a/makima/frontend/src/routes/orders.tsx +++ b/makima/frontend/src/routes/orders.tsx @@ -5,6 +5,7 @@ import { OrderList } from "../components/orders/OrderList"; import { OrderDetail } from "../components/orders/OrderDetail"; import { useOrders, useOrder } from "../hooks/useOrders"; import { useDirectives } from "../hooks/useDirectives"; +import { useDogs } from "../hooks/useDogs"; import { useAuth } from "../contexts/AuthContext"; import type { OrderStatus, OrderType, OrderPriority } from "../lib/api"; @@ -18,6 +19,7 @@ export default function OrdersPage() { const { orders, loading: listLoading, create, refresh: refreshList } = useOrders(statusFilter, typeFilter); const { order, refresh: refreshDetail, update, remove: removeOrder, linkDirective, convertToStep } = useOrder(selectedId); const { directives } = useDirectives(); + const { dogs } = useDogs(order?.directiveId ?? undefined); const [showCreate, setShowCreate] = useState(false); const [newTitle, setNewTitle] = useState(""); @@ -228,6 +230,7 @@ export default function OrdersPage() { <OrderDetail order={order} directives={directives} + dogs={dogs} onUpdate={handleUpdate} onDelete={handleDelete} onLinkDirective={handleLinkDirective} |
