feat: implement list detail page with item management

- Created /list/[id] page showing all items in a list
- Added ItemCard component with completion toggle, priority badge, and due date display
- Implemented InlineItemCreate component for quick task creation
- Enhanced ToggleItemButton with icon variant for compact display
- Enhanced DeleteItemButton with icon variant and improved event handling
- Added overdue indicators for past due dates
- Organized items into 'To Do' and 'Completed' sections
- Implemented empty state for lists with no tasks

Requirements: 1.3, 1.5, 2.3, 5.5
This commit is contained in:
2025-12-01 19:21:42 +00:00
parent c1afb2cb6e
commit 477e378d08
6 changed files with 344 additions and 9 deletions

105
components/ItemCard.tsx Normal file
View File

@@ -0,0 +1,105 @@
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { Calendar, Tag, AlertCircle, Trash2 } from 'lucide-react'
import ToggleItemButton from './ToggleItemButton'
import DeleteItemButton from './DeleteItemButton'
interface ItemCardProps {
item: {
id: string
title: string
description: string | null
completed: boolean
priority: string
dueDate: Date | null
tags: any
notes: string | null
}
listId: string
}
export default function ItemCard({ item, listId }: ItemCardProps) {
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-700 border-green-200',
medium: 'bg-yellow-100 text-yellow-700 border-yellow-200',
high: 'bg-red-100 text-red-700 border-red-200',
}
const priorityColor = priorityColors[item.priority as keyof typeof priorityColors] || priorityColors.medium
return (
<div className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow group">
<div className="flex items-start gap-3">
{/* Completion Toggle */}
<div className="pt-1">
<ToggleItemButton itemId={item.id} completed={item.completed} variant="icon" />
</div>
{/* Content */}
<div className="flex-1 min-w-0">
<Link href={`/list/${listId}/item/${item.id}`} className="block">
<h3 className={`font-medium mb-1 ${
item.completed ? 'text-gray-500 line-through' : 'text-gray-900'
}`}>
{item.title}
</h3>
{item.description && (
<p className={`text-sm mb-2 line-clamp-2 ${
item.completed ? 'text-gray-400' : 'text-gray-600'
}`}>
{item.description}
</p>
)}
{/* Metadata */}
<div className="flex flex-wrap items-center gap-2 mt-2">
{/* Priority Badge */}
<span className={`inline-flex items-center gap-1 px-2 py-1 rounded text-xs font-medium border ${priorityColor}`}>
<AlertCircle size={12} />
{item.priority.charAt(0).toUpperCase() + item.priority.slice(1)}
</span>
{/* Due Date */}
{item.dueDate && (
<span className={`inline-flex items-center gap-1 px-2 py-1 rounded text-xs ${
isOverdue
? 'bg-red-100 text-red-700 border border-red-200 font-semibold'
: 'bg-gray-100 text-gray-700'
}`}>
<Calendar size={12} />
{new Date(item.dueDate).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
})}
{isOverdue && ' (Overdue)'}
</span>
)}
{/* Tags */}
{tags.length > 0 && (
<div className="inline-flex items-center gap-1">
<Tag size={12} className="text-gray-400" />
<span className="text-xs text-gray-600">
{tags.slice(0, 2).join(', ')}
{tags.length > 2 && ` +${tags.length - 2}`}
</span>
</div>
)}
</div>
</Link>
</div>
{/* Delete Button */}
<div className="opacity-0 group-hover:opacity-100 transition-opacity">
<DeleteItemButton itemId={item.id} variant="icon" />
</div>
</div>
</div>
)
}