Spaces:
Sleeping
Sleeping
| import React, { useState } from "react"; | |
| import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Badge } from "@/components/ui/badge"; | |
| import { Input } from "@/components/ui/input"; | |
| import { Copy, Download, Shield } from "lucide-react"; | |
| import { toast } from "sonner"; | |
| interface Payload { | |
| id: string; | |
| name: string; | |
| score: number; | |
| tags: string[]; | |
| code?: string; | |
| } | |
| interface PayloadLibraryProps { | |
| payloads: Payload[]; | |
| onSelect?: (payload: Payload) => void; | |
| } | |
| export function PayloadLibrary({ payloads, onSelect }: PayloadLibraryProps) { | |
| const [searchTerm, setSearchTerm] = useState(""); | |
| const [selectedPayload, setSelectedPayload] = useState<Payload | null>(null); | |
| const filtered = payloads.filter(p => | |
| p.name.toLowerCase().includes(searchTerm.toLowerCase()) || | |
| p.tags.some(t => t.toLowerCase().includes(searchTerm.toLowerCase())) | |
| ); | |
| return ( | |
| <div className="space-y-4"> | |
| <div className="flex gap-2"> | |
| <Input | |
| placeholder="Search payloads..." | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| className="bg-black/50 border-green-500/20 text-white" | |
| /> | |
| </div> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| {filtered.map((payload) => ( | |
| <Card | |
| key={payload.id} | |
| className={`bg-gray-900/50 border-green-500/20 cursor-pointer hover:border-green-500/50 transition-all ${ | |
| selectedPayload?.id === payload.id ? "border-green-500 ring-1 ring-green-500" : "" | |
| }`} | |
| onClick={() => { | |
| setSelectedPayload(payload); | |
| onSelect?.(payload); | |
| }} | |
| > | |
| <CardHeader className="pb-2"> | |
| <div className="flex justify-between items-start"> | |
| <CardTitle className="text-sm text-green-400">{payload.name}</CardTitle> | |
| <Badge className="bg-cyan-500/20 text-cyan-400 text-xs">{payload.score}/100</Badge> | |
| </div> | |
| </CardHeader> | |
| <CardContent className="space-y-3"> | |
| <div className="flex flex-wrap gap-1"> | |
| {payload.tags.map((tag) => ( | |
| <Badge key={tag} className="bg-purple-500/20 text-purple-300 text-xs"> | |
| {tag} | |
| </Badge> | |
| ))} | |
| </div> | |
| <div className="flex gap-2"> | |
| <Button | |
| size="sm" | |
| variant="ghost" | |
| className="flex-1 text-xs text-green-400 hover:text-green-300" | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| toast.success("Payload copied!"); | |
| }} | |
| > | |
| <Copy size={12} className="mr-1" /> | |
| Copy | |
| </Button> | |
| <Button | |
| size="sm" | |
| variant="ghost" | |
| className="flex-1 text-xs text-cyan-400 hover:text-cyan-300" | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| toast.success("Obfuscating..."); | |
| }} | |
| > | |
| <Shield size={12} className="mr-1" /> | |
| Obfuscate | |
| </Button> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |