| // src/components/admin/AdminConversationsList.tsx | |
| import { useState } from "react"; | |
| import { Trash2, MessageSquare, Clock, RefreshCw } from "lucide-react"; | |
| import { toast } from "react-toastify"; | |
| interface AdminConversation { | |
| id: number; | |
| user1: string; | |
| user2: string; | |
| lastActivity?: string; | |
| messageCount?: number; | |
| } | |
| interface AdminConversationsListProps { | |
| conversations: AdminConversation[]; | |
| onDelete: (id: number) => Promise<void>; | |
| } | |
| export function AdminConversationsList({ conversations, onDelete }: AdminConversationsListProps) { | |
| const [loadingStates, setLoadingStates] = useState<Record<number, boolean>>({}); | |
| const handleDelete = async (id: number) => { | |
| setLoadingStates((prev) => ({ ...prev, [id]: true })); | |
| try { | |
| await onDelete(id); | |
| toast.success("Conversation deleted successfully"); | |
| } catch (err) { | |
| console.error(err); | |
| } finally { | |
| setLoadingStates((prev) => ({ ...prev, [id]: false })); | |
| } | |
| }; | |
| if (!conversations || conversations.length === 0) { | |
| return ( | |
| <div className="text-center py-12 bg-white/40 dark:bg-gray-900/40 backdrop-blur-md border border-gray-200 dark:border-gray-800 rounded-2xl text-gray-500"> | |
| No private conversations found. | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| {conversations.map((c) => { | |
| const isLoading = loadingStates[c.id] || false; | |
| return ( | |
| <div | |
| key={c.id} | |
| className="bg-white/40 dark:bg-gray-900/40 backdrop-blur-md border border-gray-200/50 dark:border-gray-800/50 rounded-2xl p-5 shadow-lg flex justify-between items-center gap-4 transition duration-200 hover:shadow-xl hover:border-gray-300 dark:hover:border-gray-700" | |
| > | |
| <div className="flex items-center gap-3.5 min-w-0"> | |
| <div className="w-11 h-11 rounded-xl bg-indigo-500/10 dark:bg-indigo-500/20 text-indigo-600 dark:text-indigo-400 flex items-center justify-center shrink-0"> | |
| <MessageSquare className="w-5 h-5" /> | |
| </div> | |
| <div className="min-w-0"> | |
| <h3 className="font-semibold text-sm truncate text-gray-850 dark:text-gray-150"> | |
| {c.user1} ↔ {c.user2} | |
| </h3> | |
| <div className="flex items-center gap-2 mt-1 text-[10px] font-medium text-gray-400"> | |
| <span>{c.messageCount ?? 0} messages</span> | |
| <span>•</span> | |
| <span className="flex items-center gap-0.5"> | |
| <Clock className="w-3 h-3" /> | |
| Last activity: {c.lastActivity ? new Date(c.lastActivity).toLocaleDateString() : "unknown"} | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="shrink-0"> | |
| {isLoading ? ( | |
| <RefreshCw className="w-4 h-4 text-gray-400 animate-spin" /> | |
| ) : ( | |
| <button | |
| onClick={() => handleDelete(c.id)} | |
| className="p-2 text-gray-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-500/10 rounded-xl transition duration-200" | |
| title="Delete Conversation" | |
| > | |
| <Trash2 className="w-4.5 h-4.5" /> | |
| </button> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| ); | |
| } |
Xet Storage Details
- Size:
- 3.42 kB
- Xet hash:
- 74a2a2283f82699472ea844d78d09b714218eba3ec6e1081adf8d3eb609ebf56
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.