diff --git a/app/components/Navigation.tsx b/app/components/Navigation.tsx new file mode 100644 index 0000000..7eb64ee --- /dev/null +++ b/app/components/Navigation.tsx @@ -0,0 +1,54 @@ +'use client' + +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { useAuth } from '../contexts/AuthContext' + +export default function Navigation() { + const { isAuthenticated, logout } = useAuth() + const pathname = usePathname() + + if (pathname === '/login' || pathname === '/dashboard') { + return null + } + + return ( + + + + + + Agent Manager + + + + + {isAuthenticated ? ( + + + Dashboard + + + Logout + + + ) : ( + + Login + + )} + + + + + ) +} \ No newline at end of file diff --git a/app/contexts/AuthContext.tsx b/app/contexts/AuthContext.tsx new file mode 100644 index 0000000..f2303af --- /dev/null +++ b/app/contexts/AuthContext.tsx @@ -0,0 +1,57 @@ +'use client' + +import React, { createContext, useContext, useState } from 'react' + +interface User { + username: string +} + +interface AuthContextType { + user: User | null + login: (username: string, password: string) => boolean + logout: () => void + isAuthenticated: boolean +} + +const AuthContext = createContext(undefined) + +export function AuthProvider({ children }: { children: React.ReactNode }) { + const [user, setUser] = useState(() => { + if (typeof window !== 'undefined') { + const savedUser = localStorage.getItem('user') + return savedUser ? JSON.parse(savedUser) : null + } + return null + }) + + const login = (username: string, password: string): boolean => { + if (username === 'admin' && password === 'Mallory122907Fucku2u2') { + const userData = { username } + setUser(userData) + localStorage.setItem('user', JSON.stringify(userData)) + return true + } + return false + } + + const logout = () => { + setUser(null) + localStorage.removeItem('user') + } + + const isAuthenticated = !!user + + return ( + + {children} + + ) +} + +export function useAuth() { + const context = useContext(AuthContext) + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider') + } + return context +} \ No newline at end of file diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..145fe51 --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,185 @@ +'use client' + +import { useState } from 'react' +import { useAuth } from './contexts/AuthContext' + +interface Agent { + id: string + name: string + status: 'active' | 'inactive' | 'error' + lastRun: string + dataCollected: number +} + +export default function DashboardPage() { + const { user, logout } = useAuth() + const [agents, setAgents] = useState([ + { + id: '1', + name: 'News Collector', + status: 'active', + lastRun: '2024-01-03 10:30:00', + dataCollected: 1250 + }, + { + id: '2', + name: 'Social Media Monitor', + status: 'inactive', + lastRun: '2024-01-02 15:45:00', + dataCollected: 890 + }, + { + id: '3', + name: 'Market Data Scraper', + status: 'error', + lastRun: '2024-01-01 09:00:00', + dataCollected: 567 + } + ]) + + const toggleAgentStatus = (id: string) => { + setAgents(prev => prev.map(agent => { + if (agent.id === id) { + const newStatus = agent.status === 'active' ? 'inactive' : 'active' + return { ...agent, status: newStatus } + } + return agent + })) + } + + const getStatusColor = (status: Agent['status']) => { + switch (status) { + case 'active': return 'text-green-500' + case 'inactive': return 'text-gray-500' + case 'error': return 'text-red-500' + } + } + + const getStatusBg = (status: Agent['status']) => { + switch (status) { + case 'active': return 'bg-green-100 dark:bg-green-900' + case 'inactive': return 'bg-gray-100 dark:bg-gray-900' + case 'error': return 'bg-red-100 dark:bg-red-900' + } + } + + return ( + + + + + + Agent Dashboard + + + + Welcome, {user?.username} + + + Logout + + + + + + + + + + Information Collection Agents + + + {agents.map((agent) => ( + + + + {agent.name} + + + {agent.status} + + + + + + Last Run: + {agent.lastRun} + + + Data Collected: + {agent.dataCollected} + + + + + toggleAgentStatus(agent.id)} + className={`flex-1 rounded-md px-3 py-2 text-sm font-medium transition-colors ${ + agent.status === 'active' + ? 'bg-red-500 text-white hover:bg-red-600' + : 'bg-green-500 text-white hover:bg-green-600' + }`} + > + {agent.status === 'active' ? 'Stop' : 'Start'} + + + Configure + + + + ))} + + + + + + + Quick Actions + + + + Add New Agent + + + Run All Agents + + + Export Data + + + + + + + System Overview + + + + Total Agents: + {agents.length} + + + Active Agents: + + {agents.filter(a => a.status === 'active').length} + + + + Total Data Points: + + {agents.reduce((sum, a) => sum + a.dataCollected, 0)} + + + + + + + + ) +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..f29f177 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import { AuthProvider } from "./contexts/AuthContext"; +import Navigation from "./components/Navigation"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -27,7 +29,10 @@ export default function RootLayout({ - {children} + + + {children} +