From ae3bc57de7a240c3c8ab15080b405e8ea3e16ccb Mon Sep 17 00:00:00 2001 From: soryu Date: Thu, 5 Mar 2026 23:25:40 +0000 Subject: WIP: heartbeat checkpoint --- .../frontend/src/components/directives/DOGList.tsx | 381 +++++++++++++++++++++ .../src/components/directives/DirectiveDetail.tsx | 72 +++- .../frontend/src/components/orders/OrderDetail.tsx | 91 +++++ makima/frontend/src/routes/directives.tsx | 8 + makima/frontend/src/routes/orders.tsx | 3 + 5 files changed, 542 insertions(+), 13 deletions(-) create mode 100644 makima/frontend/src/components/directives/DOGList.tsx 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 = { + 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; + onUpdateDog: (dogId: string, req: { name?: string; description?: string | null; status?: DOGStatus }) => Promise; + onDeleteDog: (dogId: string) => Promise; + onPickUpOrders: (dogId: string) => Promise; +} + +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 ( +
+ Loading DOGs... +
+ ); + } + + return ( +
+ {/* Header + Create button */} +
+ + Order Groups ({dogs.length}) + + +
+ + {/* Create form */} + {showCreate && ( +
+
+ + 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]" + /> +
+
+ +