draupnir4all-web

git clone git://archive.git.mtrnord.blog/MTRNord/draupnir4all-web.git
Log | Files | Refs | README | LICENSE

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 }