Files
director-app/ui/src/components/DirectorQueue.tsx

83 lines
3.0 KiB
TypeScript

import type { QueueItem } from '../lib/api'
import { relativeTime, absoluteTime } from '../lib/time'
const statusColors: Record<string, string> = {
pending_verification: 'text-yellow-600',
ready_for_approval: 'text-blue-600 dark:text-blue-400',
returned: 'text-red-600',
approved: 'text-green-600',
redirected: 'text-orange-600',
}
function QueueItemCard({ item, onReview }: { item: QueueItem; onReview: () => void }) {
return (
<div className="group p-3 rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:border-gray-400 dark:hover:border-gray-500 transition-colors">
<div className="flex items-start justify-between gap-3 mb-1">
<span className="font-medium text-sm text-gray-900 dark:text-gray-100 leading-snug">
{item.plan_title || `Plan #${item.plan_id}`}
</span>
<span className={`shrink-0 font-mono text-[10px] uppercase tracking-wider ${statusColors[item.status] ?? ''}`}>
{item.status.replace(/_/g, ' ')}
</span>
</div>
{item.task_title && (
<div className="font-mono text-[10px] text-gray-500 dark:text-gray-400 mb-1">
{item.task_title}
</div>
)}
{item.verification_notes && (
<div className="text-xs text-gray-600 dark:text-gray-400 mt-2 leading-relaxed line-clamp-2">
{item.verification_notes}
</div>
)}
<div className="flex items-center justify-between mt-3 pt-2 border-t border-gray-100 dark:border-gray-700/50">
<span className="font-mono text-[10px] tabular text-gray-400 dark:text-gray-500" title={absoluteTime(item.submitted_at)}>
submitted {relativeTime(item.submitted_at)}
</span>
{item.status === 'ready_for_approval' && (
<button
onClick={onReview}
className="font-mono text-[10px] uppercase tracking-wider px-3 py-1 rounded bg-blue-600 hover:bg-blue-700 text-white cursor-pointer"
>
review
</button>
)}
</div>
</div>
)
}
export function DirectorQueue({
items,
onReview,
}: {
items: QueueItem[]
onReview: (item: QueueItem) => void
}) {
return (
<div className="rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-5">
<div className="flex items-baseline justify-between mb-4">
<h2 className="font-display text-xl font-medium text-gray-900 dark:text-gray-100">
Director Queue
</h2>
<span className="font-mono text-[10px] uppercase tracking-wider text-gray-400">
{items.length} {items.length === 1 ? 'item' : 'items'}
</span>
</div>
{items.length === 0 ? (
<p className="font-mono text-xs text-gray-500 italic">No items awaiting review</p>
) : (
<div className="space-y-2">
{items.map((item) => (
<QueueItemCard key={item.id} item={item} onReview={() => onReview(item)} />
))}
</div>
)}
</div>
)
}