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>
);
}