From 9aadbc7958d39d181c0dd0600e2b7c30bb6c391a Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sat, 14 Feb 2026 21:29:26 +0000
Subject: Makima system improvements: Orders, directive questions, PR creation
 fix, bug fixes (#62)

* feat: soryu-co/soryu - makima: Fix directive goal update bug - stale closure issue

* WIP: heartbeat checkpoint

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Create Orders database schema and backend API

* feat: soryu-co/soryu - makima: Fix task Claude instance not receiving user inputs from input box

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Build Orders frontend page replacing the Board page

* WIP: heartbeat checkpoint

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Fix directive PR creation system
---
 .../frontend/src/components/orders/OrderList.tsx   | 188 +++++++++++++++++++++
 1 file changed, 188 insertions(+)
 create mode 100644 makima/frontend/src/components/orders/OrderList.tsx

(limited to 'makima/frontend/src/components/orders/OrderList.tsx')

diff --git a/makima/frontend/src/components/orders/OrderList.tsx b/makima/frontend/src/components/orders/OrderList.tsx
new file mode 100644
index 0000000..76ac7a7
--- /dev/null
+++ b/makima/frontend/src/components/orders/OrderList.tsx
@@ -0,0 +1,188 @@
+import { useState, useMemo } from "react";
+import type { Order, OrderStatus, OrderPriority, OrderType } from "../../lib/api";
+
+const STATUS_BADGE: Record<OrderStatus, { 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 PRIORITY_COLOR: Record<OrderPriority, string> = {
+  critical: "bg-red-400",
+  high: "bg-orange-400",
+  medium: "bg-yellow-400",
+  low: "bg-[#75aafc]",
+  none: "bg-[#556677]",
+};
+
+const TYPE_BADGE: Record<OrderType, { color: string; label: string }> = {
+  feature: { color: "text-[#75aafc] border-[rgba(117,170,252,0.3)]", label: "FEAT" },
+  bug: { color: "text-red-400 border-red-800", label: "BUG" },
+  spike: { color: "text-yellow-400 border-yellow-800", label: "SPIKE" },
+  chore: { color: "text-[#7788aa] border-[#2a3a5a]", label: "CHORE" },
+  improvement: { color: "text-emerald-400 border-emerald-800", label: "IMPROVE" },
+};
+
+interface OrderListProps {
+  orders: Order[];
+  selectedId: string | null;
+  onSelect: (id: string) => void;
+  onCreate: () => void;
+  statusFilter: OrderStatus | undefined;
+  onStatusFilter: (s: OrderStatus | undefined) => void;
+  typeFilter: OrderType | undefined;
+  onTypeFilter: (t: OrderType | undefined) => void;
+}
+
+const STATUS_OPTIONS: (OrderStatus | "all")[] = ["all", "open", "in_progress", "done", "archived"];
+const TYPE_OPTIONS: (OrderType | "all")[] = ["all", "feature", "bug", "spike", "chore", "improvement"];
+
+export function OrderList({
+  orders,
+  selectedId,
+  onSelect,
+  onCreate,
+  statusFilter,
+  onStatusFilter,
+  typeFilter,
+  onTypeFilter,
+}: OrderListProps) {
+  const [search, setSearch] = useState("");
+
+  const filtered = useMemo(() => {
+    if (!search.trim()) return orders;
+    const q = search.toLowerCase();
+    return orders.filter(
+      (o) =>
+        o.title.toLowerCase().includes(q) ||
+        (o.description && o.description.toLowerCase().includes(q)) ||
+        o.labels.some((l) => l.toLowerCase().includes(q)),
+    );
+  }, [orders, search]);
+
+  return (
+    <div className="flex flex-col h-full">
+      {/* Header */}
+      <div className="flex items-center justify-between px-3 py-2 border-b border-dashed border-[rgba(117,170,252,0.2)]">
+        <span className="text-[11px] font-mono text-[#9bc3ff] uppercase tracking-wide">
+          Orders
+        </span>
+        <button
+          type="button"
+          onClick={onCreate}
+          className="text-[11px] font-mono text-[#75aafc] hover:text-white bg-transparent border border-[rgba(117,170,252,0.3)] rounded px-2 py-0.5 hover:border-[rgba(117,170,252,0.6)] transition-colors"
+        >
+          + New
+        </button>
+      </div>
+
+      {/* Search */}
+      <div className="px-3 py-1.5 border-b border-[rgba(117,170,252,0.1)]">
+        <input
+          value={search}
+          onChange={(e) => setSearch(e.target.value)}
+          placeholder="Search orders..."
+          className="w-full bg-transparent border-none outline-none text-[11px] font-mono text-white placeholder:text-[#556677]"
+        />
+      </div>
+
+      {/* Filters */}
+      <div className="px-3 py-1.5 border-b border-[rgba(117,170,252,0.1)] flex flex-col gap-1">
+        <div className="flex items-center gap-1 flex-wrap">
+          <span className="text-[9px] font-mono text-[#556677] uppercase w-[38px] shrink-0">
+            Status
+          </span>
+          {STATUS_OPTIONS.map((s) => (
+            <button
+              key={s}
+              type="button"
+              onClick={() => onStatusFilter(s === "all" ? undefined : s)}
+              className={`text-[9px] font-mono px-1.5 py-0.5 rounded transition-colors ${
+                (s === "all" && !statusFilter) || s === statusFilter
+                  ? "text-white bg-[rgba(117,170,252,0.15)] border border-[rgba(117,170,252,0.4)]"
+                  : "text-[#556677] hover:text-[#7788aa] border border-transparent"
+              }`}
+            >
+              {s === "all" ? "ALL" : s === "in_progress" ? "WIP" : s.toUpperCase()}
+            </button>
+          ))}
+        </div>
+        <div className="flex items-center gap-1 flex-wrap">
+          <span className="text-[9px] font-mono text-[#556677] uppercase w-[38px] shrink-0">
+            Type
+          </span>
+          {TYPE_OPTIONS.map((t) => (
+            <button
+              key={t}
+              type="button"
+              onClick={() => onTypeFilter(t === "all" ? undefined : t)}
+              className={`text-[9px] font-mono px-1.5 py-0.5 rounded transition-colors ${
+                (t === "all" && !typeFilter) || t === typeFilter
+                  ? "text-white bg-[rgba(117,170,252,0.15)] border border-[rgba(117,170,252,0.4)]"
+                  : "text-[#556677] hover:text-[#7788aa] border border-transparent"
+              }`}
+            >
+              {t === "all" ? "ALL" : t.toUpperCase()}
+            </button>
+          ))}
+        </div>
+      </div>
+
+      {/* List */}
+      <div className="flex-1 overflow-y-auto">
+        {filtered.length === 0 ? (
+          <div className="px-3 py-6 text-center text-[#556677] font-mono text-[11px]">
+            No orders found
+          </div>
+        ) : (
+          filtered.map((o) => {
+            const statusBadge = STATUS_BADGE[o.status] || STATUS_BADGE.open;
+            const typeBadge = TYPE_BADGE[o.orderType] || TYPE_BADGE.feature;
+            const priorityColor = PRIORITY_COLOR[o.priority] || PRIORITY_COLOR.none;
+
+            return (
+              <button
+                key={o.id}
+                type="button"
+                onClick={() => onSelect(o.id)}
+                className={`w-full text-left px-3 py-2.5 border-b border-[rgba(117,170,252,0.1)] hover:bg-[rgba(117,170,252,0.05)] transition-colors ${
+                  selectedId === o.id ? "bg-[rgba(117,170,252,0.1)]" : ""
+                }`}
+              >
+                <div className="flex items-start gap-2 mb-1">
+                  {/* Priority dot */}
+                  <span
+                    className={`w-2 h-2 rounded-full ${priorityColor} shrink-0 mt-[3px]`}
+                    title={o.priority}
+                  />
+                  <span className="text-[12px] font-mono text-white truncate flex-1">
+                    {o.title}
+                  </span>
+                </div>
+                <div className="flex items-center gap-1.5 pl-4">
+                  <span
+                    className={`text-[9px] font-mono ${statusBadge.color} border rounded px-1.5 py-0.5`}
+                  >
+                    {statusBadge.label}
+                  </span>
+                  <span
+                    className={`text-[9px] font-mono ${typeBadge.color} border rounded px-1.5 py-0.5`}
+                  >
+                    {typeBadge.label}
+                  </span>
+                  {o.labels.length > 0 && (
+                    <span className="text-[9px] font-mono text-[#556677] truncate">
+                      {o.labels.slice(0, 2).join(", ")}
+                      {o.labels.length > 2 && ` +${o.labels.length - 2}`}
+                    </span>
+                  )}
+                </div>
+              </button>
+            );
+          })
+        )}
+      </div>
+    </div>
+  );
+}
-- 
cgit v1.2.3

