import { useState } from "react";
import type {
Order,
OrderStatus,
OrderPriority,
OrderType,
UpdateOrderRequest,
DirectiveSummary,
DirectiveOrderGroup,
} 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: "bg-purple-400/20 text-purple-400 border-purple-800", label: "UNDER REVIEW" },
done: { color: "text-emerald-400 border-emerald-800", label: "DONE" },
archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" },
};
const PRIORITY_OPTIONS: { value: OrderPriority; color: string; label: string }[] = [
{ value: "critical", color: "text-red-400 border-red-800", label: "Critical" },
{ value: "high", color: "text-orange-400 border-orange-800", label: "High" },
{ value: "medium", color: "text-yellow-400 border-yellow-800", label: "Medium" },
{ value: "low", color: "text-[#75aafc] border-[rgba(117,170,252,0.4)]", label: "Low" },
{ value: "none", color: "text-[#556677] border-[#2a3a5a]", label: "None" },
];
const TYPE_OPTIONS: { value: OrderType; color: string; label: string }[] = [
{ value: "feature", color: "text-[#75aafc]", label: "Feature" },
{ value: "bug", color: "text-red-400", label: "Bug" },
{ value: "spike", color: "text-yellow-400", label: "Spike" },
{ value: "chore", color: "text-[#7788aa]", label: "Chore" },
{ value: "improvement", color: "text-emerald-400", label: "Improvement" },
];
const STATUS_OPTIONS: OrderStatus[] = ["open", "in_progress", "under_review", "done", "archived"];
interface OrderDetailProps {
order: Order;
directives: DirectiveSummary[];
dogs: DirectiveOrderGroup[];
onUpdate: (req: UpdateOrderRequest) => Promise<void>;
onDelete: () => void;
onLinkDirective: (directiveId: string) => Promise<void>;
onConvertToStep: () => Promise<void>;
onRefresh: () => void;
}
export function OrderDetail({
order,
directives,
dogs,
onUpdate,
onDelete,
onLinkDirective,
onConvertToStep,
onRefresh,
}: OrderDetailProps) {
const [editingTitle, setEditingTitle] = useState(false);
const [titleText, setTitleText] = useState(order.title);
const [editingDesc, setEditingDesc] = useState(false);
const [descText, setDescText] = useState(order.description || "");
const [editingLabels, setEditingLabels] = useState(false);
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];
const currentType = TYPE_OPTIONS.find((t) => t.value === order.orderType) || TYPE_OPTIONS[0];
const handleTitleSave = async () => {
if (titleText.trim() && titleText !== order.title) {
await onUpdate({ title: titleText.trim() });
}
setEditingTitle(false);
};
const handleDescSave = async () => {
const newDesc = descText.trim() || null;
if (newDesc !== order.description) {
await onUpdate({ description: newDesc });
}
setEditingDesc(false);
};
const handleLabelsSave = async () => {
const newLabels = labelsText
.split(",")
.map((l) => l.trim())
.filter((l) => l.length > 0);
await onUpdate({ labels: newLabels });
setEditingLabels(false);
};
const handleStatusChange = async (status: OrderStatus) => {
await onUpdate({ status });
};
const handlePriorityChange = async (priority: OrderPriority) => {
await onUpdate({ priority });
};
const handleTypeChange = async (orderType: OrderType) => {
await onUpdate({ orderType });
};
const handleLinkDirective = async (directiveId: string) => {
await onLinkDirective(directiveId);
setShowLinkDirective(false);
};
return (
<div className="flex flex-col h-full overflow-y-auto">
{/* Header */}
<div className="px-4 py-3 border-b border-dashed border-[rgba(117,170,252,0.2)]">
<div className="flex items-center justify-between mb-2">
{editingTitle ? (
<div className="flex-1 flex items-center gap-2 pr-2">
<input
value={titleText}
onChange={(e) => setTitleText(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") handleTitleSave();
if (e.key === "Escape") setEditingTitle(false);
}}
autoFocus
className="flex-1 bg-[#0a1628] border border-[rgba(117,170,252,0.2)] rounded px-2 py-1 text-[14px] font-mono text-white"
/>
<button
type="button"
onClick={handleTitleSave}
className="text-[10px] font-mono text-emerald-400 hover:text-emerald-300"
>
[save]
</button>
<button
type="button"
onClick={() => setEditingTitle(false)}
className="text-[10px] font-mono text-[#556677] hover:text-white"
>
[cancel]
</button>
</div>
) : (
<h2
className="text-[14px] font-mono text-white font-medium truncate pr-2 cursor-pointer hover:text-[#9bc3ff]"
onClick={() => {
setTitleText(order.title);
setEditingTitle(true);
}}
>
{order.title}
</h2>
)}
<div className="flex items-center gap-2 shrink-0">
<span
className={`text-[10px] font-mono ${badge.color} border rounded px-2 py-0.5`}
>
{badge.label}
</span>
<button
type="button"
onClick={onRefresh}
className="text-[10px] font-mono text-[#7788aa] hover:text-white"
title="Refresh"
>
[refresh]
</button>
</div>
</div>
{/* Type + Priority inline */}
<div className="flex items-center gap-3 mb-2">
<span className={`text-[10px] font-mono ${currentType.color}`}>
{currentType.label}
</span>
<span className="text-[10px] font-mono text-[#2a3a5a]">/</span>
<span className={`text-[10px] font-mono ${currentPriority.color} border rounded px-1.5 py-0.5`}>
{currentPriority.label}
</span>
</div>
{/* Linked entities */}
{order.directiveId && (
<div className="text-[10px] font-mono text-[#556677] mb-1 truncate">
Directive: <a href={`/directives/${order.directiveId}`} className="text-[#75aafc] hover:text-white underline">
{order.directiveName || order.directiveId.slice(0, 8) + "..."}
</a>
</div>
)}
{order.directiveStepId && (
<div className="text-[10px] font-mono text-[#556677] mb-1 truncate">
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">
<button
type="button"
onClick={onDelete}
className="text-[10px] font-mono text-red-400 hover:text-red-300 border border-red-800 rounded px-2 py-1 ml-auto"
>
Delete
</button>
</div>
</div>
{/* Status selector */}
<div className="px-4 py-3 border-b border-[rgba(117,170,252,0.1)]">
<div className="flex items-center justify-between mb-1.5">
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide">
Status
</span>
</div>
<div className="flex gap-1.5 flex-wrap">
{STATUS_OPTIONS.map((s) => {
const sBadge = STATUS_BADGE[s];
return (
<button
key={s}
type="button"
onClick={() => handleStatusChange(s)}
className={`text-[10px] font-mono border rounded px-2 py-0.5 transition-colors ${
s === order.status
? `${sBadge.color} bg-[rgba(117,170,252,0.1)]`
: "text-[#556677] border-[#2a3a5a] hover:text-[#7788aa]"
}`}
>
{sBadge.label}
</button>
);
})}
</div>
</div>
{/* Priority selector */}
<div className="px-4 py-3 border-b border-[rgba(117,170,252,0.1)]">
<div className="flex items-center justify-between mb-1.5">
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide">
Priority
</span>
</div>
<div className="flex gap-1.5 flex-wrap">
{PRIORITY_OPTIONS.map((p) => (
<button
key={p.value}
type="button"
onClick={() => handlePriorityChange(p.value)}
className={`text-[10px] font-mono border rounded px-2 py-0.5 transition-colors ${
p.value === order.priority
? `${p.color} bg-[rgba(117,170,252,0.1)]`
: "text-[#556677] border-[#2a3a5a] hover:text-[#7788aa]"
}`}
>
{p.label}
</button>
))}
</div>
</div>
{/* Type selector */}
<div className="px-4 py-3 border-b border-[rgba(117,170,252,0.1)]">
<div className="flex items-center justify-between mb-1.5">
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide">
Type
</span>
</div>
<div className="flex gap-1.5 flex-wrap">
{TYPE_OPTIONS.map((t) => (
<button
key={t.value}
type="button"
onClick={() => handleTypeChange(t.value)}
className={`text-[10px] font-mono border rounded px-2 py-0.5 transition-colors ${
t.value === order.orderType
? `${t.color} border-current bg-[rgba(117,170,252,0.1)]`
: "text-[#556677] border-[#2a3a5a] hover:text-[#7788aa]"
}`}
>
{t.label}
</button>
))}
</div>
</div>
{/* Description */}
<div className="px-4 py-3 border-b border-[rgba(117,170,252,0.1)]">
<div className="flex items-center justify-between mb-1">
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide">
Description
</span>
{!editingDesc && (
<button
type="button"
onClick={() => {
setDescText(order.description || "");
setEditingDesc(true);
}}
className="text-[9px] font-mono text-[#556677] hover:text-[#75aafc]"
>
[edit]
</button>
)}
</div>
{editingDesc ? (
<div className="flex flex-col gap-1.5">
<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.5 text-[11px] font-mono text-white resize-y min-h-[80px]"
rows={4}
autoFocus
/>
<div className="flex gap-1.5">
<button
type="button"
onClick={handleDescSave}
className="text-[10px] font-mono text-emerald-400 hover:text-emerald-300 border border-emerald-800 rounded px-2 py-0.5"
>
Save
</button>
<button
type="button"
onClick={() => setEditingDesc(false)}
className="text-[10px] font-mono text-[#7788aa] hover:text-white border border-[#2a3a5a] rounded px-2 py-0.5"
>
Cancel
</button>
</div>
</div>
) : (
<p className="text-[11px] font-mono text-[#c0d0e0] whitespace-pre-wrap">
{order.description || <span className="text-[#556677] italic">No description</span>}
</p>
)}
</div>
{/* Labels */}
<div className="px-4 py-3 border-b border-[rgba(117,170,252,0.1)]">
<div className="flex items-center justify-between mb-1">
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide">
Labels
</span>
{!editingLabels && (
<button
type="button"
onClick={() => {
setLabelsText(order.labels.join(", "));
setEditingLabels(true);
}}
className="text-[9px] font-mono text-[#556677] hover:text-[#75aafc]"
>
[edit]
</button>
)}
</div>
{editingLabels ? (
<div className="flex flex-col gap-1.5">
<input
value={labelsText}
onChange={(e) => setLabelsText(e.target.value)}
placeholder="label1, label2, ..."
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"
autoFocus
/>
<div className="flex gap-1.5">
<button
type="button"
onClick={handleLabelsSave}
className="text-[10px] font-mono text-emerald-400 hover:text-emerald-300 border border-emerald-800 rounded px-2 py-0.5"
>
Save
</button>
<button
type="button"
onClick={() => setEditingLabels(false)}
className="text-[10px] font-mono text-[#7788aa] hover:text-white border border-[#2a3a5a] rounded px-2 py-0.5"
>
Cancel
</button>
</div>
</div>
) : (
<div className="flex gap-1 flex-wrap">
{order.labels.length > 0 ? (
order.labels.map((l) => (
<span
key={l}
className="text-[10px] font-mono text-[#9bc3ff] bg-[rgba(117,170,252,0.1)] border border-[rgba(117,170,252,0.2)] rounded px-1.5 py-0.5"
>
{l}
</span>
))
) : (
<span className="text-[10px] font-mono text-[#556677] italic">No labels</span>
)}
</div>
)}
</div>
{/* Actions */}
<div className="px-4 py-3 flex-1">
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide block mb-2">
Actions
</span>
<div className="flex flex-col gap-2">
{/* Link to Directive */}
<div>
<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] 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>
{/* 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
type="button"
onClick={() => onConvertToStep()}
className="text-[10px] font-mono text-yellow-400 hover:text-yellow-300 border border-yellow-800 rounded px-2 py-1 w-full text-left"
>
Convert to Directive Step
</button>
)}
</div>
</div>
{/* Metadata */}
<div className="px-4 py-2 border-t border-[rgba(117,170,252,0.1)]">
<div className="flex items-center justify-between text-[9px] font-mono text-[#556677]">
<span>Created {new Date(order.createdAt).toLocaleDateString()}</span>
<span>Updated {new Date(order.updatedAt).toLocaleDateString()}</span>
</div>
</div>
</div>
);
}