Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 2x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 22x 1x 88x 1x 10x 30x 5x | import { Search, MessageCircle } from "lucide-react";
import { cn } from "../../lib/utils";
import type { WaConversation } from "../../lib/api/whatsapp";
type ConversationListProps = {
conversations: WaConversation[];
selectedId: number | null;
onSelect: (id: number) => void;
search: string;
onSearchChange: (value: string) => void;
contactTypeFilter: string;
onContactTypeFilterChange: (value: string) => void;
isLoading: boolean;
};
const contactTypeTabs = [
{ value: "", label: "All" },
{ value: "pro", label: "Pros" },
{ value: "customer", label: "Customers" },
{ value: "unknown", label: "Unknown" },
];
function formatRelativeTime(dateStr: string | null): string {
Iif (!dateStr) return "";
const date = new Date(dateStr);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
Iif (diffMins < 1) return "now";
Iif (diffMins < 60) return `${diffMins}m`;
const diffHours = Math.floor(diffMins / 60);
Iif (diffHours < 24) return `${diffHours}h`;
const diffDays = Math.floor(diffHours / 24);
Iif (diffDays < 7) return `${diffDays}d`;
return date.toLocaleDateString("en-IN", { day: "numeric", month: "short" });
}
export function ConversationList({
conversations,
selectedId,
onSelect,
search,
onSearchChange,
contactTypeFilter,
onContactTypeFilterChange,
isLoading,
}: ConversationListProps) {
return (
<div className="flex flex-col h-full border-r border-border-default">
{/* Search */}
<div className="p-3 border-b border-border-default">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-foreground-subtle" />
<input
type="text"
value={search}
onChange={(e) => onSearchChange(e.target.value)}
placeholder="Search conversations..."
className="w-full rounded-md border border-border-default bg-background-default pl-9 pr-3 py-2 text-sm placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-whatsapp-green focus:border-transparent"
/>
</div>
</div>
{/* Contact Type Tabs */}
<div className="flex border-b border-border-default">
{contactTypeTabs.map((tab) => (
<button
key={tab.value}
type="button"
onClick={() => onContactTypeFilterChange(tab.value)}
className={cn(
"flex-1 px-2 py-2 text-xs font-medium transition-colors",
contactTypeFilter === tab.value
? "text-whatsapp-green border-b-2 border-whatsapp-green"
: "text-foreground-muted hover:text-foreground-default",
)}
>
{tab.label}
</button>
))}
</div>
{/* Conversation List */}
<div className="flex-1 overflow-y-auto">
{isLoading ? (
<div className="p-4 space-y-3">
{[1, 2, 3, 4, 5].map((i) => (
<div
key={i}
className="h-16 bg-background-muted rounded-md animate-pulse"
/>
))}
</div>
) : conversations.length === 0 ? (
<div className="flex flex-col items-center justify-center h-48 text-foreground-muted">
<MessageCircle className="h-8 w-8 mb-2" />
<p className="text-sm">No conversations found</p>
</div>
) : (
conversations.map((conv) => (
<button
key={conv.id}
type="button"
onClick={() => onSelect(conv.id)}
className={cn(
"w-full text-left px-4 py-3 border-b border-border-default transition-colors",
selectedId === conv.id
? "bg-whatsapp-bg/30 dark:bg-whatsapp-green/10"
: "hover:bg-background-muted",
)}
>
<div className="flex items-start justify-between gap-2">
<div className="flex items-center gap-3 min-w-0">
<div
className={cn(
"h-10 w-10 rounded-full flex items-center justify-center flex-shrink-0 text-sm font-medium",
conv.contactType === "pro"
? "bg-info-light text-info"
: conv.contactType === "customer"
? "bg-warning-light text-warning"
: "bg-background-muted text-foreground-muted",
)}
>
{(conv.contactName || conv.phoneNumber)
.charAt(0)
.toUpperCase()}
</div>
<div className="min-w-0">
<p className="text-sm font-medium text-foreground-default truncate">
{conv.contactName || conv.phoneNumber}
</p>
<p className="text-xs text-foreground-muted truncate">
{conv.phoneNumber}
</p>
</div>
</div>
<div className="flex flex-col items-end flex-shrink-0">
<span className="text-xs text-foreground-subtle">
{formatRelativeTime(conv.lastMessageAt)}
</span>
{conv.unreadCount > 0 && (
<span className="mt-1 inline-flex items-center justify-center min-w-[20px] h-5 rounded-full bg-whatsapp-green px-1.5 text-xs font-medium text-foreground-inverse">
{conv.unreadCount}
</span>
)}
</div>
</div>
</button>
))
)}
</div>
</div>
);
}
|