- Reduced padding from p-4/p-5 to p-3/p-3.5 for more compact cards - Moved priority badge to the right side next to title - Changed items-start to items-center for better alignment - Description now limited to 1 line instead of 2 - Metadata section only shows if there's a due date or tags - Cleaner, more compact layout
114 lines
4.4 KiB
TypeScript
114 lines
4.4 KiB
TypeScript
'use client'
|
|
|
|
import { memo } from 'react'
|
|
import Link from 'next/link'
|
|
import { Calendar, Tag, AlertCircle } 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
|
|
}
|
|
|
|
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-3 sm:p-3.5 hover:shadow-md active:shadow-lg transition-all group">
|
|
<div className="flex items-center gap-2 sm:gap-3">
|
|
{/* Completion Toggle */}
|
|
<div className="flex-shrink-0">
|
|
<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">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<h3 className={`font-medium text-sm sm:text-base ${
|
|
item.completed ? 'text-gray-500 line-through' : 'text-gray-900'
|
|
}`}>
|
|
{item.title}
|
|
</h3>
|
|
|
|
{/* Priority Badge - moved to right */}
|
|
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium border flex-shrink-0 ${priorityColor}`}>
|
|
<AlertCircle size={12} className="flex-shrink-0" />
|
|
<span className="hidden xs:inline sm:inline">{item.priority.charAt(0).toUpperCase() + item.priority.slice(1)}</span>
|
|
<span className="xs:hidden sm:hidden">{item.priority.charAt(0).toUpperCase()}</span>
|
|
</span>
|
|
</div>
|
|
|
|
{item.description && (
|
|
<p className={`text-xs sm:text-sm mt-1 line-clamp-1 ${
|
|
item.completed ? 'text-gray-400' : 'text-gray-600'
|
|
}`}>
|
|
{item.description}
|
|
</p>
|
|
)}
|
|
|
|
{/* Metadata */}
|
|
{(item.dueDate || tags.length > 0) && (
|
|
<div className="flex flex-wrap items-center gap-1.5 sm:gap-2 mt-1.5">
|
|
{/* Due Date */}
|
|
{item.dueDate && (
|
|
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs whitespace-nowrap ${
|
|
isOverdue
|
|
? 'bg-red-100 text-red-700 border border-red-200 font-semibold'
|
|
: 'bg-gray-100 text-gray-700'
|
|
}`}>
|
|
<Calendar size={12} className="flex-shrink-0" />
|
|
{new Date(item.dueDate).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
})}
|
|
{isOverdue && <span className="hidden sm:inline"> (Overdue)</span>}
|
|
{isOverdue && <span className="sm:hidden">!</span>}
|
|
</span>
|
|
)}
|
|
|
|
{/* Tags */}
|
|
{tags.length > 0 && (
|
|
<div className="inline-flex items-center gap-1">
|
|
<Tag size={12} className="text-gray-400 flex-shrink-0" />
|
|
<span className="text-xs text-gray-600 truncate max-w-[120px] sm:max-w-none">
|
|
{tags.slice(0, 2).join(', ')}
|
|
{tags.length > 2 && ` +${tags.length - 2}`}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Delete Button */}
|
|
<div className="opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity flex-shrink-0">
|
|
<DeleteItemButton itemId={item.id} variant="icon" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default memo(ItemCard)
|