Files
Todo/components/ItemDetail.tsx
Paul Bailey 282a0295ec feat: implement item detail page and form components
- Created ItemForm component with fields for all metadata (priority, due date, tags, notes)
- Created ItemDetail component to display all item information
- Built /list/[id]/item/[itemId] page for viewing full item details
- Built /list/[id]/item/[itemId]/edit page for editing items
- Added form validation for item inputs (title required, length limits)
- Created DeleteItemButton and ToggleItemButton helper components
- Enhanced createItem action to support full metadata on creation
- Added getItem action to fetch individual items with list data

Requirements: 1.1, 1.2, 5.1, 5.2, 5.3, 5.4
2025-12-01 19:16:37 +00:00

164 lines
5.1 KiB
TypeScript

import { Calendar, Tag, FileText, AlertCircle, CheckCircle2, Circle } from 'lucide-react'
import Link from 'next/link'
interface ItemDetailProps {
item: {
id: string
title: string
description: string | null
completed: boolean
priority: string
dueDate: Date | null
tags: any
notes: string | null
createdAt: Date
updatedAt: Date
listId: string
}
listTitle: string
}
export default function ItemDetail({ item, listTitle }: ItemDetailProps) {
const tags = Array.isArray(item.tags) ? item.tags : []
const isOverdue = item.dueDate && new Date(item.dueDate) < new Date() && !item.completed
const priorityColors = {
low: 'bg-green-100 text-green-800',
medium: 'bg-yellow-100 text-yellow-800',
high: 'bg-red-100 text-red-800',
}
const priorityColor = priorityColors[item.priority as keyof typeof priorityColors] || priorityColors.medium
return (
<div className="space-y-6">
{/* Header */}
<div>
<div className="flex items-center gap-2 text-sm text-gray-500 mb-2">
<Link href={`/list/${item.listId}`} className="hover:text-blue-600">
{listTitle}
</Link>
<span>/</span>
<span>Task Details</span>
</div>
<div className="flex items-start gap-3">
<div className="pt-1">
{item.completed ? (
<CheckCircle2 className="text-green-500" size={24} />
) : (
<Circle className="text-gray-400" size={24} />
)}
</div>
<div className="flex-1">
<h1 className={`text-2xl font-bold ${item.completed ? 'text-gray-500 line-through' : 'text-gray-900'}`}>
{item.title}
</h1>
{item.completed && (
<p className="text-sm text-green-600 mt-1">Completed</p>
)}
</div>
</div>
</div>
{/* Metadata Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Priority */}
<div className="bg-gray-50 rounded-lg p-4">
<div className="flex items-center gap-2 text-sm text-gray-600 mb-1">
<AlertCircle size={16} />
<span className="font-medium">Priority</span>
</div>
<span className={`inline-block px-3 py-1 rounded-full text-sm font-medium ${priorityColor}`}>
{item.priority.charAt(0).toUpperCase() + item.priority.slice(1)}
</span>
</div>
{/* Due Date */}
<div className="bg-gray-50 rounded-lg p-4">
<div className="flex items-center gap-2 text-sm text-gray-600 mb-1">
<Calendar size={16} />
<span className="font-medium">Due Date</span>
</div>
{item.dueDate ? (
<div>
<p className={`text-gray-900 ${isOverdue ? 'text-red-600 font-semibold' : ''}`}>
{new Date(item.dueDate).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</p>
{isOverdue && (
<p className="text-sm text-red-600 mt-1">Overdue</p>
)}
</div>
) : (
<p className="text-gray-500">No due date</p>
)}
</div>
</div>
{/* Description */}
{item.description && (
<div className="bg-gray-50 rounded-lg p-4">
<h2 className="text-sm font-medium text-gray-600 mb-2">Description</h2>
<p className="text-gray-900 whitespace-pre-wrap">{item.description}</p>
</div>
)}
{/* Tags */}
{tags.length > 0 && (
<div className="bg-gray-50 rounded-lg p-4">
<div className="flex items-center gap-2 text-sm font-medium text-gray-600 mb-3">
<Tag size={16} />
<span>Tags</span>
</div>
<div className="flex flex-wrap gap-2">
{tags.map((tag: string) => (
<span
key={tag}
className="px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm"
>
{tag}
</span>
))}
</div>
</div>
)}
{/* Notes */}
{item.notes && (
<div className="bg-gray-50 rounded-lg p-4">
<div className="flex items-center gap-2 text-sm font-medium text-gray-600 mb-3">
<FileText size={16} />
<span>Notes</span>
</div>
<p className="text-gray-900 whitespace-pre-wrap">{item.notes}</p>
</div>
)}
{/* Timestamps */}
<div className="border-t pt-4 text-sm text-gray-500 space-y-1">
<p>
Created: {new Date(item.createdAt).toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
</p>
<p>
Updated: {new Date(item.updatedAt).toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
</p>
</div>
</div>
)
}