letsbe-hub-dashboard/src/components/email/message-list.tsx

143 lines
4.2 KiB
TypeScript

'use client'
import Link from 'next/link'
import { Star, Paperclip } from 'lucide-react'
import { cn } from '@/lib/utils'
import { Skeleton } from '@/components/ui/skeleton'
export interface MessageListItem {
uid: number
from: { name: string; address: string } | null
subject: string
date: string
flags: string[]
preview: string
}
interface MessageListProps {
messages: MessageListItem[]
folder: string
loading?: boolean
selectedUid?: number
}
function formatDate(dateStr: string): string {
const date = new Date(dateStr)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24))
if (diffDays === 0) {
return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })
}
if (diffDays === 1) return 'Yesterday'
if (diffDays < 7) {
return date.toLocaleDateString(undefined, { weekday: 'short' })
}
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
}
function senderDisplay(from: { name: string; address: string } | null): string {
if (!from) return 'Unknown'
return from.name || from.address
}
function senderInitial(from: { name: string; address: string } | null): string {
const display = senderDisplay(from)
return display.charAt(0).toUpperCase()
}
export function MessageList({ messages, folder, loading, selectedUid }: MessageListProps) {
if (loading) {
return (
<div className="divide-y">
{Array.from({ length: 8 }).map((_, i) => (
<div key={i} className="flex items-start gap-3 p-4">
<Skeleton className="h-9 w-9 rounded-full shrink-0" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-4 w-48" />
<Skeleton className="h-3 w-64" />
</div>
<Skeleton className="h-3 w-12" />
</div>
))}
</div>
)
}
if (messages.length === 0) {
return (
<div className="flex flex-col items-center justify-center py-16 text-muted-foreground">
<p className="text-sm">No messages</p>
</div>
)
}
return (
<div className="divide-y">
{messages.map((msg) => {
const isRead = msg.flags.includes('\\Seen')
const isStarred = msg.flags.includes('\\Flagged')
const encodedFolder = encodeURIComponent(folder)
return (
<Link
key={msg.uid}
href={`/email/${encodedFolder}/${msg.uid}`}
className={cn(
'flex items-start gap-3 p-4 transition-colors hover:bg-accent/50',
!isRead && 'bg-accent/20',
selectedUid === msg.uid && 'bg-accent'
)}
>
<div
className={cn(
'flex h-9 w-9 items-center justify-center rounded-full shrink-0 text-sm font-medium',
isRead
? 'bg-muted text-muted-foreground'
: 'bg-primary text-primary-foreground'
)}
>
{senderInitial(msg.from)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span
className={cn(
'text-sm truncate',
!isRead && 'font-semibold'
)}
>
{senderDisplay(msg.from)}
</span>
{isStarred && (
<Star className="h-3.5 w-3.5 fill-yellow-400 text-yellow-400 shrink-0" />
)}
</div>
<p
className={cn(
'text-sm truncate',
!isRead ? 'font-medium text-foreground' : 'text-muted-foreground'
)}
>
{msg.subject}
</p>
{msg.preview && (
<p className="text-xs text-muted-foreground truncate mt-0.5">
{msg.preview}
</p>
)}
</div>
<span className="text-xs text-muted-foreground whitespace-nowrap shrink-0">
{formatDate(msg.date)}
</span>
</Link>
)
})}
</div>
)
}