Spaces:
Sleeping
Sleeping
| import React, { useState } from "react"; | |
| import { trpc } from "@/lib/trpc"; | |
| import { useAuth } from "@/_core/hooks/useAuth"; | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | |
| import { Badge } from "@/components/ui/badge"; | |
| import { Zap, Code2, Cpu, TrendingUp, Plus, Play, Activity } from "lucide-react"; | |
| import { IterationChart } from "@/components/IterationChart"; | |
| import { CodeEditor } from "@/components/CodeEditor"; | |
| import { PayloadLibrary } from "@/components/PayloadLibrary"; | |
| import { NewProjectDialog } from "@/components/NewProjectDialog"; | |
| import ChatInterface from "./ChatInterface"; | |
| export default function Dashboard() { | |
| const { user } = useAuth(); | |
| const projectsQuery = trpc.projects.list.useQuery(); | |
| const [selectedProject, setSelectedProject] = useState<number | null>(null); | |
| const [refreshKey, setRefreshKey] = useState(0); | |
| const iterationsQuery = trpc.projects.getIterations.useQuery( | |
| { projectId: selectedProject as number }, | |
| { enabled: !!selectedProject } | |
| ); | |
| const stats = [ | |
| { label: "Active Projects", value: projectsQuery.data?.length || 0, icon: Code2, color: "from-green-400 to-emerald-500" }, | |
| { label: "Completed", value: projectsQuery.data?.filter(p => p.status === "completed").length || 0, icon: TrendingUp, color: "from-cyan-400 to-blue-500" }, | |
| { label: "Processing", value: projectsQuery.data?.filter(p => p.status === "in_progress").length || 0, icon: Cpu, color: "from-purple-400 to-pink-500" }, | |
| { label: "Avg Score", value: "85", icon: Zap, color: "from-orange-400 to-red-500" }, | |
| ]; | |
| const handleProjectCreated = () => { | |
| projectsQuery.refetch(); | |
| setRefreshKey(prev => prev + 1); | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-black text-white"> | |
| {/* Header */} | |
| <header className="border-b border-green-500/20 bg-black/50 backdrop-blur-sm sticky top-0 z-40"> | |
| <div className="container mx-auto px-4 py-4 flex justify-between items-center"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-green-400 to-cyan-400 flex items-center justify-center"> | |
| <Zap className="text-black" size={20} /> | |
| </div> | |
| <div> | |
| <h1 className="text-xl font-bold bg-gradient-to-r from-green-400 via-cyan-400 to-blue-500 bg-clip-text text-transparent"> | |
| Hacking Factory | |
| </h1> | |
| <p className="text-xs text-gray-400">AI Code Generation & Evaluation</p> | |
| </div> | |
| </div> | |
| <div className="text-sm text-gray-400"> | |
| Welcome, <span className="text-green-400 font-semibold">{user?.name || "User"}</span> | |
| </div> | |
| </div> | |
| </header> | |
| {/* Main Content */} | |
| <main className="container mx-auto px-4 py-8"> | |
| {/* Stats Grid */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8"> | |
| {stats.map((stat, idx) => { | |
| const Icon = stat.icon; | |
| return ( | |
| <Card key={idx} className="bg-gray-900/50 border-green-500/20 hover:border-green-500/50 transition-colors"> | |
| <CardContent className="pt-6"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-gray-400 text-sm">{stat.label}</p> | |
| <p className="text-3xl font-bold text-white mt-2">{stat.value}</p> | |
| </div> | |
| <div className={`p-3 rounded-lg bg-gradient-to-br ${stat.color} opacity-20`}> | |
| <Icon className="text-white" size={24} /> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ); | |
| })} | |
| </div> | |
| {/* Projects Section */} | |
| <Tabs defaultValue="projects" className="space-y-4"> | |
| <TabsList className="bg-gray-900/50 border border-green-500/20"> | |
| <TabsTrigger value="projects" className="data-[state=active]:bg-green-500/20 data-[state=active]:text-green-400"> | |
| Projects | |
| </TabsTrigger> | |
| <TabsTrigger value="chat" className="data-[state=active]:bg-cyan-500/20 data-[state=active]:text-cyan-400"> | |
| AI Chat | |
| </TabsTrigger> | |
| <TabsTrigger value="library" className="data-[state=active]:bg-purple-500/20 data-[state=active]:text-purple-400"> | |
| Code Library | |
| </TabsTrigger> | |
| </TabsList> | |
| {/* Projects Tab */} | |
| <TabsContent value="projects" className="space-y-4"> | |
| <div className="flex justify-between items-center mb-4"> | |
| <h2 className="text-xl font-bold text-white">Your Projects</h2> | |
| <NewProjectDialog onProjectCreated={handleProjectCreated} /> | |
| </div> | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| <div className="space-y-4"> | |
| {projectsQuery.data?.map((project) => ( | |
| <Card | |
| key={project.id} | |
| className={`bg-gray-900/50 border-green-500/20 hover:border-green-500/50 transition-all cursor-pointer ${selectedProject === project.id ? 'border-green-500 ring-1 ring-green-500' : ''}`} | |
| onClick={() => setSelectedProject(project.id)} | |
| > | |
| <CardHeader> | |
| <div className="flex justify-between items-start"> | |
| <div> | |
| <CardTitle className="text-green-400">{project.name}</CardTitle> | |
| <CardDescription className="text-gray-400 mt-1"> | |
| Mode: <span className="text-cyan-400 font-mono">{project.mode}</span> | |
| </CardDescription> | |
| </div> | |
| <Badge | |
| className={`${ | |
| project.status === "completed" | |
| ? "bg-green-500/20 text-green-400" | |
| : project.status === "in_progress" | |
| ? "bg-cyan-500/20 text-cyan-400" | |
| : "bg-gray-500/20 text-gray-400" | |
| }`} | |
| > | |
| {project.status} | |
| </Badge> | |
| </div> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="flex justify-between items-center"> | |
| <div className="text-sm text-gray-400"> | |
| Score: <span className="text-green-400 font-bold">{project.finalScore}/100</span> | |
| </div> | |
| <Button variant="ghost" size="sm" className="text-cyan-400 hover:text-cyan-300"> | |
| <Play size={16} className="mr-2" /> | |
| View | |
| </Button> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ))} | |
| {!projectsQuery.data || projectsQuery.data.length === 0 && ( | |
| <Card className="bg-gray-900/50 border-gray-500/20"> | |
| <CardContent className="pt-6 text-center text-gray-500"> | |
| No projects yet. Create one to get started! | |
| </CardContent> | |
| </Card> | |
| )} | |
| </div> | |
| {/* Detail Panel */} | |
| <div className="space-y-4"> | |
| {selectedProject ? ( | |
| <Card className="bg-gray-900/50 border-cyan-500/20 h-full"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2 text-cyan-400"> | |
| <Activity size={20} /> | |
| Iteration Progress | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| {iterationsQuery.data && iterationsQuery.data.length > 0 ? ( | |
| <div className="space-y-6"> | |
| <IterationChart data={iterationsQuery.data} /> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div className="p-3 rounded-lg bg-black/50 border border-green-500/20"> | |
| <p className="text-xs text-gray-400">Best Score</p> | |
| <p className="text-xl font-bold text-green-400"> | |
| {Math.max(...iterationsQuery.data.map(i => i.score || 0))}/100 | |
| </p> | |
| </div> | |
| <div className="p-3 rounded-lg bg-black/50 border border-cyan-500/20"> | |
| <p className="text-xs text-gray-400">Total Iterations</p> | |
| <p className="text-xl font-bold text-cyan-400"> | |
| {iterationsQuery.data.length} | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="h-64 flex items-center justify-center text-gray-500 border border-dashed border-gray-700 rounded-lg"> | |
| No iterations recorded yet | |
| </div> | |
| )} | |
| </CardContent> | |
| </Card> | |
| ) : ( | |
| <div className="h-full flex items-center justify-center text-gray-500 border border-dashed border-gray-700 rounded-lg"> | |
| Select a project to view progress | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </TabsContent> | |
| {/* Chat Tab */} | |
| <TabsContent value="chat" className="space-y-4"> | |
| <Card className="bg-gray-900/50 border-cyan-500/20"> | |
| <CardHeader> | |
| <CardTitle className="text-cyan-400">AI Chat Interface</CardTitle> | |
| <CardDescription className="text-gray-400"> | |
| Generate and evaluate code with multiple AI models | |
| </CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <ChatInterface /> | |
| </CardContent> | |
| </Card> | |
| </TabsContent> | |
| {/* Library Tab */} | |
| <TabsContent value="library" className="space-y-4"> | |
| <Card className="bg-gray-900/50 border-purple-500/20"> | |
| <CardHeader> | |
| <CardTitle className="text-purple-400">Code Library</CardTitle> | |
| <CardDescription className="text-gray-400"> | |
| Manage your reference code files and templates | |
| </CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <PayloadLibrary | |
| payloads={[ | |
| { id: "p1", name: "Reverse Shell", score: 92, tags: ["shell", "network"] }, | |
| { id: "p2", name: "Privilege Escalation", score: 88, tags: ["privilege", "windows"] }, | |
| { id: "p3", name: "Data Exfiltration", score: 85, tags: ["data", "stealth"] }, | |
| ]} | |
| /> | |
| </CardContent> | |
| </Card> | |
| </TabsContent> | |
| </Tabs> | |
| </main> | |
| </div> | |
| ); | |
| } | |