bans-list.tsx (7341B)
1 "use client" 2 3 import { EBanTypes, PolicyList } from "@/app/dashboard/mockData" 4 import { Ban, Pen, PenOff } from "lucide-react" 5 import { ChangeEvent, useState } from "react" 6 import { ScrollArea } from "../ui/scroll-area" 7 import { Button } from "../ui/button" 8 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select" 9 import { Input } from "../ui/input" 10 11 interface BanListProps { 12 policyLists: PolicyList[], 13 } 14 15 export default function BansList({ policyLists }: BanListProps) { 16 const [selectedPolicyList, setSelectedPolicyList] = useState("global") 17 const [filter, setFilter] = useState("all") 18 const [searchTerm, setSearchTerm] = useState("") 19 const selectedList = policyLists.find((list) => list.id === selectedPolicyList) ?? policyLists[0] 20 21 if (!selectedList) { 22 // TODO: Error handling 23 return <div className="flex h-screen w-full items-center justify-center">Loading...</div> 24 } 25 26 const entries = selectedList.entries.filter((entry) => { 27 if (filter === "all") return true; 28 if (filter === "users") return entry.type === EBanTypes.User; 29 if (filter === "servers") return entry.type === EBanTypes.Server; 30 if (filter === "rooms") return entry.type === EBanTypes.Room; 31 return false; 32 }).filter((entry) => { 33 if (!searchTerm) return true; 34 const searchLower = searchTerm.toLowerCase(); 35 return entry.target.toLowerCase().includes(searchLower) || entry.reason.toLowerCase().includes(searchLower); 36 }); 37 38 return ( 39 <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> 40 <div className="space-y-4"> 41 <h3 className="text-sm font-medium text-gray-400">Policy Lists</h3> 42 <div className="space-y-2"> 43 {policyLists.length > 0 ? ( 44 policyLists.map((list) => ( 45 <div 46 key={list.id} 47 className={`p-3 rounded-md border cursor-pointer transition-colors ${selectedPolicyList === list.id 48 ? "bg-gray-800 border-purple-600" 49 : "bg-gray-900 border-gray-800 hover:border-gray-700" 50 }`} 51 onClick={() => setSelectedPolicyList(list.id)} 52 > 53 <div className="flex items-center justify-between mb-1"> 54 <span className="font-medium">{list.name}</span> 55 {list.readOnly ? ( 56 <PenOff className=" text-gray-400">Read Only</PenOff> 57 ) : ( 58 <Pen className="text-green-300">Editable</Pen> 59 )} 60 </div> 61 <p className="text-xs text-gray-400">{list.description}</p> 62 <p className="text-xs text-gray-500 mt-1">{list.entries.length} entries</p> 63 </div> 64 )) 65 ) : ( 66 <div className="flex flex-col items-center justify-center p-8 text-center"> 67 <Ban className="h-8 w-8 text-gray-500 mb-2" /> 68 <p className="text-gray-400">No policy lists available</p> 69 </div> 70 )} 71 </div> 72 </div> 73 74 <div className="md:col-span-2 space-y-4"> 75 <div className="flex items-center justify-between"> 76 <h3 className="text-sm font-medium text-gray-400"> 77 {selectedList.name || "Policy List"} Entries 78 </h3> 79 <div className="flex items-center gap-2"> 80 <Input onChange={(e: ChangeEvent) => { 81 const value = (e.target as HTMLInputElement).value; 82 setSearchTerm(value) 83 }} value={searchTerm} placeholder="Search entries..." className="h-8 w-[200px] bg-gray-900 border-gray-800" /> 84 {/* Filter Dropdown */} 85 <Select defaultValue="all" onValueChange={setFilter}> 86 <SelectTrigger className="h-8 w-[120px] bg-gray-900 border-gray-800"> 87 <SelectValue placeholder="Filter" /> 88 </SelectTrigger> 89 <SelectContent className="bg-gray-900 border-gray-800"> 90 <SelectItem value="all">All Bans</SelectItem> 91 <SelectItem value="users">Users</SelectItem> 92 <SelectItem value="servers">Servers</SelectItem> 93 <SelectItem value="rooms">Rooms</SelectItem> 94 </SelectContent> 95 </Select> 96 </div> 97 </div> 98 99 <ScrollArea className="h-[400px] rounded-md border border-gray-800"> 100 <div className="p-4 space-y-3"> 101 {selectedList && entries && entries.length > 0 ? ( 102 entries.map((entry) => ( 103 <div key={entry.id} className="p-3 rounded-md bg-gray-900 border border-gray-800"> 104 <div className="flex items-start justify-between"> 105 <div> 106 <p className="font-medium">{entry.target}</p> 107 <p className="text-sm text-gray-400 mt-1">{entry.reason}</p> 108 <p className="text-xs text-gray-500 mt-1"> 109 {new Date(entry.timestamp).toLocaleString()} 110 </p> 111 </div> 112 {!selectedList.readOnly && ( 113 <Button 114 variant="ghost" 115 size="sm" 116 className="h-8 w-8 p-0 text-gray-400 hover:text-red-400" 117 > 118 <Ban className="h-4 w-4" /> 119 <span className="sr-only">Remove</span> 120 </Button> 121 )} 122 </div> 123 </div> 124 )) 125 ) : ( 126 <div className="flex flex-col items-center justify-center h-full p-6 text-center"> 127 <Ban className="h-8 w-8 text-gray-500 mb-2" /> 128 <p className="text-gray-400">No ban entries found</p> 129 </div> 130 )} 131 </div> 132 </ScrollArea> 133 </div> 134 </div> 135 ) 136 }