summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/orders
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/components/orders')
-rw-r--r--makima/frontend/src/components/orders/OrderDetail.tsx117
-rw-r--r--makima/frontend/src/components/orders/OrderList.tsx5
2 files changed, 95 insertions, 27 deletions
diff --git a/makima/frontend/src/components/orders/OrderDetail.tsx b/makima/frontend/src/components/orders/OrderDetail.tsx
index 9d4c00c..1b8d76e 100644
--- a/makima/frontend/src/components/orders/OrderDetail.tsx
+++ b/makima/frontend/src/components/orders/OrderDetail.tsx
@@ -11,6 +11,7 @@ import type {
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" },
+ under_review: { color: "text-amber-400 border-amber-800", label: "UNDER REVIEW" },
done: { color: "text-emerald-400 border-emerald-800", label: "DONE" },
archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" },
};
@@ -31,7 +32,7 @@ const TYPE_OPTIONS: { value: OrderType; color: string; label: string }[] = [
{ value: "improvement", color: "text-emerald-400", label: "Improvement" },
];
-const STATUS_OPTIONS: OrderStatus[] = ["open", "in_progress", "done", "archived"];
+const STATUS_OPTIONS: OrderStatus[] = ["open", "in_progress", "under_review", "done", "archived"];
interface OrderDetailProps {
order: Order;
@@ -59,6 +60,7 @@ export function OrderDetail({
const [editingLabels, setEditingLabels] = useState(false);
const [labelsText, setLabelsText] = useState(order.labels.join(", "));
const [showLinkDirective, setShowLinkDirective] = useState(false);
+ const [directiveSearch, setDirectiveSearch] = useState("");
const badge = STATUS_BADGE[order.status] || STATUS_BADGE.open;
const currentPriority = PRIORITY_OPTIONS.find((p) => p.value === order.priority) || PRIORITY_OPTIONS[4];
@@ -405,31 +407,96 @@ export function OrderDetail({
<div className="flex flex-col gap-2">
{/* Link to Directive */}
<div>
- <button
- type="button"
- onClick={() => setShowLinkDirective(!showLinkDirective)}
- className="text-[10px] font-mono text-[#75aafc] hover:text-white border border-[rgba(117,170,252,0.3)] rounded px-2 py-1 w-full text-left"
- >
- Link to Directive
- </button>
+ <div className="flex items-center gap-1.5">
+ <button
+ type="button"
+ onClick={() => {
+ setShowLinkDirective(!showLinkDirective);
+ setDirectiveSearch("");
+ }}
+ 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.directiveId ? "Change Directive" : "Link to Directive"}
+ </button>
+ {order.directiveId && (
+ <button
+ type="button"
+ onClick={() => onUpdate({ directiveId: null, directiveStepId: null })}
+ className="text-[10px] font-mono text-red-400 hover:text-red-300 border border-red-800 rounded px-2 py-1"
+ title="Unlink directive"
+ >
+ Unlink
+ </button>
+ )}
+ </div>
{showLinkDirective && (
- <div className="mt-1 border border-[rgba(117,170,252,0.2)] bg-[#0a1525] max-h-32 overflow-y-auto rounded">
- {directives.length === 0 ? (
- <div className="px-3 py-2 text-[10px] font-mono text-[#556677]">
- No directives available
- </div>
- ) : (
- directives.map((d) => (
- <button
- key={d.id}
- type="button"
- onClick={() => handleLinkDirective(d.id)}
- className="w-full text-left px-3 py-1.5 text-[10px] font-mono text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.1)] border-b border-[rgba(117,170,252,0.1)] last:border-b-0"
- >
- {d.title}
- </button>
- ))
- )}
+ <div className="mt-1 border border-[rgba(117,170,252,0.2)] bg-[#0a1525] rounded">
+ <div className="px-2 py-1.5 border-b border-[rgba(117,170,252,0.1)]">
+ <input
+ type="text"
+ value={directiveSearch}
+ onChange={(e) => setDirectiveSearch(e.target.value)}
+ placeholder="Search directives..."
+ autoFocus
+ className="w-full bg-transparent border-none outline-none text-[10px] font-mono text-[#75aafc] placeholder-[#556677]"
+ />
+ </div>
+ <div className="max-h-32 overflow-y-auto">
+ {directives.length === 0 ? (
+ <div className="px-3 py-2 text-[10px] font-mono text-[#556677]">
+ No directives available
+ </div>
+ ) : (
+ (() => {
+ const filtered = directives.filter((d) =>
+ d.title.toLowerCase().includes(directiveSearch.toLowerCase())
+ );
+ if (filtered.length === 0) {
+ return (
+ <div className="px-3 py-2 text-[10px] font-mono text-[#556677]">
+ No matching directives
+ </div>
+ );
+ }
+ return filtered.map((d) => {
+ const isLinked = d.id === order.directiveId;
+ const statusColors: Record<string, string> = {
+ draft: "text-[#556677] border-[#2a3a5a]",
+ active: "text-emerald-400 border-emerald-800",
+ idle: "text-[#7788aa] border-[#2a3a5a]",
+ paused: "text-yellow-400 border-yellow-800",
+ archived: "text-[#556677] border-[#2a3a5a]",
+ };
+ const sColor = statusColors[d.status] || statusColors.draft;
+ return (
+ <button
+ key={d.id}
+ type="button"
+ onClick={() => handleLinkDirective(d.id)}
+ 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 ${
+ isLinked ? "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.title}</span>
+ {isLinked && (
+ <span className="shrink-0 text-[8px] text-emerald-400">●</span>
+ )}
+ </div>
+ {d.repositoryUrl && (
+ <div className="text-[8px] text-[#556677] truncate mt-0.5">
+ {d.repositoryUrl}
+ </div>
+ )}
+ </button>
+ );
+ });
+ })()
+ )}
+ </div>
</div>
)}
</div>
diff --git a/makima/frontend/src/components/orders/OrderList.tsx b/makima/frontend/src/components/orders/OrderList.tsx
index 1d279f7..3d63c54 100644
--- a/makima/frontend/src/components/orders/OrderList.tsx
+++ b/makima/frontend/src/components/orders/OrderList.tsx
@@ -4,6 +4,7 @@ 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" },
+ under_review: { color: "text-amber-400 border-amber-800", label: "REVIEW" },
done: { color: "text-emerald-400 border-emerald-800", label: "DONE" },
archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" },
};
@@ -35,7 +36,7 @@ interface OrderListProps {
onTypeFilter: (t: OrderType | undefined) => void;
}
-const STATUS_OPTIONS: (OrderStatus | "all")[] = ["all", "open", "in_progress", "done", "archived"];
+const STATUS_OPTIONS: (OrderStatus | "all")[] = ["all", "open", "in_progress", "under_review", "done", "archived"];
const TYPE_OPTIONS: (OrderType | "all")[] = ["all", "feature", "bug", "spike", "chore", "improvement"];
export function OrderList({
@@ -105,7 +106,7 @@ export function OrderList({
: "text-[#556677] hover:text-[#7788aa] border border-transparent"
}`}
>
- {s === "all" ? "ALL" : s === "in_progress" ? "WIP" : s.toUpperCase()}
+ {s === "all" ? "ALL" : s === "in_progress" ? "WIP" : s === "under_review" ? "REVIEW" : s.toUpperCase()}
</button>
))}
</div>