Note
code presented here, readme coming soon
feedback welcomed
| import { useState } from 'react'; | |
| import { Sidebar } from './components/Sidebar'; | |
| import { Dashboard } from './components/Dashboard'; | |
| import { AppsManager } from './components/AppsManager'; | |
| import { DomainsManager } from './components/DomainsManager'; | |
| import { ServerManager } from './components/ServerManager'; | |
| import { NodeManager } from './components/NodeManager'; | |
| import { ContentManager } from './components/ContentManager'; | |
| import { UserManager } from './components/UserManager'; | |
| import { CodespaceManager } from './components/CodespaceManager'; | |
| import { GitHubManager } from './components/GitHubManager'; | |
| import { VSCodeManager } from './components/VSCodeManager'; | |
| export default function App() { | |
| const [activeSection, setActiveSection] = useState('dashboard'); | |
| const renderContent = () => { | |
| switch (activeSection) { | |
| case 'dashboard': | |
| return <Dashboard />; | |
| case 'apps': | |
| return <AppsManager />; | |
| case 'domains': | |
| return <DomainsManager />; | |
| case 'server': | |
| return <ServerManager />; | |
| case 'nodejs': | |
| return <NodeManager />; | |
| case 'content': | |
| return <ContentManager />; | |
| case 'users': | |
| return <UserManager />; | |
| case 'codespace': | |
| return <CodespaceManager />; | |
| case 'github': | |
| return <GitHubManager />; | |
| case 'vscode': | |
| return <VSCodeManager />; | |
| default: | |
| return <Dashboard />; | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800"> | |
| <div className="flex"> | |
| <Sidebar | |
| activeSection={activeSection} | |
| onSectionChange={setActiveSection} | |
| /> | |
| <main className="flex-1 ml-64"> | |
| <div className="p-8"> | |
| {renderContent()} | |
| </div> | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Globe, | |
| Plus, | |
| Play, | |
| Pause, | |
| Settings, | |
| Trash2, | |
| ExternalLink, | |
| GitBranch, | |
| Activity, | |
| Database | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| export function AppsManager() { | |
| const [apps] = useState([ | |
| { | |
| id: 1, | |
| name: 'Portfolio Website', | |
| domain: 'portfolio.dev', | |
| status: 'running', | |
| framework: 'Next.js', | |
| version: 'v13.4.0', | |
| lastDeploy: '2 hours ago', | |
| traffic: '1.2K visits', | |
| repo: 'github.com/user/portfolio' | |
| }, | |
| { | |
| id: 2, | |
| name: 'E-commerce Store', | |
| domain: 'shop.example.com', | |
| status: 'running', | |
| framework: 'React', | |
| version: 'v18.2.0', | |
| lastDeploy: '1 day ago', | |
| traffic: '5.8K visits', | |
| repo: 'github.com/user/ecommerce' | |
| }, | |
| { | |
| id: 3, | |
| name: 'Blog Platform', | |
| domain: 'blog.dev', | |
| status: 'stopped', | |
| framework: 'Vue.js', | |
| version: 'v3.3.0', | |
| lastDeploy: '3 days ago', | |
| traffic: '890 visits', | |
| repo: 'github.com/user/blog' | |
| }, | |
| { | |
| id: 4, | |
| name: 'API Server', | |
| domain: 'api.service.com', | |
| status: 'running', | |
| framework: 'Node.js', | |
| version: 'v18.17.0', | |
| lastDeploy: '5 hours ago', | |
| traffic: '12.4K requests', | |
| repo: 'github.com/user/api' | |
| } | |
| ]); | |
| const getStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'running': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'stopped': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| case 'deploying': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getFrameworkColor = (framework: string) => { | |
| const colors = { | |
| 'Next.js': 'bg-black text-white', | |
| 'React': 'bg-blue-500 text-white', | |
| 'Vue.js': 'bg-green-500 text-white', | |
| 'Node.js': 'bg-green-600 text-white' | |
| }; | |
| return colors[framework as keyof typeof colors] || 'bg-gray-500 text-white'; | |
| }; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Apps & Sites</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage your deployed applications and websites</p> | |
| </div> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| Deploy New App | |
| </Button> | |
| </div> | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {apps.map((app) => ( | |
| <Card key={app.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Globe className="w-6 h-6 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{app.name}</h3> | |
| <p className="text-sm text-slate-500">{app.domain}</p> | |
| </div> | |
| </div> | |
| <Badge className={`${getStatusColor(app.status)} border`}> | |
| {app.status} | |
| </Badge> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Framework</p> | |
| <Badge className={`${getFrameworkColor(app.framework)} text-xs`}> | |
| {app.framework} | |
| </Badge> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Version</p> | |
| <p className="text-sm font-medium text-slate-700 dark:text-slate-300">{app.version}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Last Deploy</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{app.lastDeploy}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Traffic</p> | |
| <div className="flex items-center gap-1"> | |
| <Activity className="w-3 h-3 text-green-600" /> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{app.traffic}</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-2 mb-4"> | |
| <GitBranch className="w-4 h-4 text-slate-500" /> | |
| <p className="text-xs text-slate-500 truncate">{app.repo}</p> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <ExternalLink className="w-3 h-3 mr-1" /> | |
| Visit | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Database className="w-3 h-3 mr-1" /> | |
| Logs | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| {app.status === 'running' ? | |
| <Pause className="w-3 h-3" /> : | |
| <Play className="w-3 h-3" /> | |
| } | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Code, | |
| Plus, | |
| Play, | |
| Square, | |
| Terminal, | |
| Cpu, | |
| HardDrive, | |
| Wifi, | |
| GitBranch, | |
| Clock, | |
| Settings | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| export function CodespaceManager() { | |
| const [codespaces] = useState([ | |
| { | |
| id: 1, | |
| name: 'portfolio-dev', | |
| status: 'running', | |
| repository: 'user/portfolio', | |
| branch: 'main', | |
| machine: '4-core • 8GB RAM', | |
| uptime: '2h 34m', | |
| lastAccessed: '5 minutes ago', | |
| url: 'https://portfolio-dev-abc123.codespace.dev' | |
| }, | |
| { | |
| id: 2, | |
| name: 'api-server', | |
| status: 'stopped', | |
| repository: 'user/api-server', | |
| branch: 'develop', | |
| machine: '2-core • 4GB RAM', | |
| uptime: '0m', | |
| lastAccessed: '2 days ago', | |
| url: 'https://api-server-def456.codespace.dev' | |
| }, | |
| { | |
| id: 3, | |
| name: 'blog-platform', | |
| status: 'running', | |
| repository: 'user/blog', | |
| branch: 'feature/new-ui', | |
| machine: '2-core • 4GB RAM', | |
| uptime: '45m', | |
| lastAccessed: '1 hour ago', | |
| url: 'https://blog-platform-ghi789.codespace.dev' | |
| } | |
| ]); | |
| const getStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'running': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'stopped': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| case 'starting': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const totalResources = { | |
| cores: codespaces.filter(c => c.status === 'running').reduce((acc, c) => | |
| acc + parseInt(c.machine.split('-')[0]), 0), | |
| memory: codespaces.filter(c => c.status === 'running').reduce((acc, c) => | |
| acc + parseInt(c.machine.split('•')[1].trim().split('GB')[0]), 0), | |
| totalCost: 24.50 | |
| }; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Codespaces</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage your development environments</p> | |
| </div> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| New Codespace | |
| </Button> | |
| </div> | |
| {/* Resource Overview */} | |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Active Cores</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalResources.cores}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center shadow-lg"> | |
| <Cpu className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Memory Used</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalResources.memory}GB</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-green-500 to-green-600 flex items-center justify-center shadow-lg"> | |
| <HardDrive className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Monthly Cost</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">${totalResources.totalCost}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Wifi className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| </div> | |
| {/* Codespaces List */} | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {codespaces.map((codespace) => ( | |
| <Card key={codespace.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Code className="w-6 h-6 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{codespace.name}</h3> | |
| <div className="flex items-center gap-1 text-sm text-slate-500"> | |
| <GitBranch className="w-3 h-3" /> | |
| {codespace.repository} • {codespace.branch} | |
| </div> | |
| </div> | |
| </div> | |
| <Badge className={`${getStatusColor(codespace.status)} border`}> | |
| {codespace.status} | |
| </Badge> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Machine Type</p> | |
| <div className="flex items-center gap-1"> | |
| <Cpu className="w-3 h-3 text-slate-400" /> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{codespace.machine}</p> | |
| </div> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Uptime</p> | |
| <div className="flex items-center gap-1"> | |
| <Clock className="w-3 h-3 text-slate-400" /> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{codespace.uptime}</p> | |
| </div> | |
| </div> | |
| <div className="col-span-2"> | |
| <p className="text-xs text-slate-500 mb-1">Last Accessed</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{codespace.lastAccessed}</p> | |
| </div> | |
| </div> | |
| <div className="mb-4 p-3 bg-slate-50 dark:bg-slate-800 rounded-lg"> | |
| <p className="text-xs text-slate-500 mb-1">URL</p> | |
| <p className="text-sm font-mono text-slate-700 dark:text-slate-300 truncate">{codespace.url}</p> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Terminal className="w-3 h-3 mr-1" /> | |
| Open | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Code className="w-3 h-3 mr-1" /> | |
| VS Code | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| {codespace.status === 'running' ? | |
| <Square className="w-3 h-3 text-red-600" /> : | |
| <Play className="w-3 h-3 text-green-600" /> | |
| } | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| {/* Quick Actions */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-4">Quick Actions</h3> | |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Terminal className="w-6 h-6" /> | |
| <span className="text-xs">New Terminal</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <GitBranch className="w-6 h-6" /> | |
| <span className="text-xs">Clone Repo</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Settings className="w-6 h-6" /> | |
| <span className="text-xs">Settings</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Code className="w-6 h-6" /> | |
| <span className="text-xs">Templates</span> | |
| </Button> | |
| </div> | |
| </Card> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| FileText, | |
| Folder, | |
| Upload, | |
| Download, | |
| Trash2, | |
| Edit3, | |
| Image, | |
| Code, | |
| Film, | |
| Archive, | |
| Search, | |
| Plus | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Input } from './ui/input'; | |
| import { Badge } from './ui/badge'; | |
| export function ContentManager() { | |
| const [currentPath, setCurrentPath] = useState('/var/www/html'); | |
| const [searchTerm, setSearchTerm] = useState(''); | |
| const files = [ | |
| { name: 'public_html', type: 'folder', size: '-', modified: '2 days ago', permissions: '755' }, | |
| { name: 'assets', type: 'folder', size: '-', modified: '1 week ago', permissions: '755' }, | |
| { name: 'uploads', type: 'folder', size: '-', modified: '3 hours ago', permissions: '755' }, | |
| { name: 'index.html', type: 'file', size: '2.4 KB', modified: '5 hours ago', permissions: '644' }, | |
| { name: 'style.css', type: 'file', size: '15.7 KB', modified: '1 day ago', permissions: '644' }, | |
| { name: 'script.js', type: 'file', size: '8.2 KB', modified: '2 days ago', permissions: '644' }, | |
| { name: 'logo.png', type: 'image', size: '45.8 KB', modified: '1 week ago', permissions: '644' }, | |
| { name: 'video.mp4', type: 'video', size: '125 MB', modified: '3 days ago', permissions: '644' }, | |
| { name: 'backup.zip', type: 'archive', size: '89.3 MB', modified: '1 week ago', permissions: '644' }, | |
| { name: 'config.json', type: 'file', size: '1.2 KB', modified: '6 hours ago', permissions: '600' }, | |
| ]; | |
| const getFileIcon = (type: string) => { | |
| switch (type) { | |
| case 'folder': | |
| return <Folder className="w-5 h-5 text-blue-600" />; | |
| case 'image': | |
| return <Image className="w-5 h-5 text-green-600" />; | |
| case 'video': | |
| return <Film className="w-5 h-5 text-purple-600" />; | |
| case 'archive': | |
| return <Archive className="w-5 h-5 text-orange-600" />; | |
| default: | |
| return <FileText className="w-5 h-5 text-slate-600" />; | |
| } | |
| }; | |
| const getFileTypeColor = (type: string) => { | |
| switch (type) { | |
| case 'folder': | |
| return 'bg-blue-100 text-blue-800 border-blue-200'; | |
| case 'image': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'video': | |
| return 'bg-purple-100 text-purple-800 border-purple-200'; | |
| case 'archive': | |
| return 'bg-orange-100 text-orange-800 border-orange-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const filteredFiles = files.filter(file => | |
| file.name.toLowerCase().includes(searchTerm.toLowerCase()) | |
| ); | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Content Manager</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage your server files and content</p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="outline" className="border-dashed"> | |
| <Upload className="w-4 h-4 mr-2" /> | |
| Upload Files | |
| </Button> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| New Folder | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Navigation and Search */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <div className="flex items-center gap-2"> | |
| <Code className="w-5 h-5 text-slate-600 dark:text-slate-400" /> | |
| <span className="text-sm font-medium text-slate-700 dark:text-slate-300">Current Path:</span> | |
| <span className="text-sm text-slate-600 dark:text-slate-400 font-mono bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded"> | |
| {currentPath} | |
| </span> | |
| </div> | |
| <div className="relative"> | |
| <Search className="w-4 h-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400" /> | |
| <Input | |
| placeholder="Search files..." | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| className="pl-10 w-64" | |
| /> | |
| </div> | |
| </div> | |
| </Card> | |
| {/* File List */} | |
| <Card className="border-0 shadow-lg overflow-hidden"> | |
| <div className="overflow-x-auto"> | |
| <table className="w-full"> | |
| <thead className="bg-slate-50 dark:bg-slate-800"> | |
| <tr> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Name</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Type</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Size</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Modified</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Permissions</th> | |
| <th className="text-right p-4 font-medium text-slate-700 dark:text-slate-300">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {filteredFiles.map((file, index) => ( | |
| <tr key={index} className="border-t border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <td className="p-4"> | |
| <div className="flex items-center gap-3"> | |
| {getFileIcon(file.type)} | |
| <span className="font-medium text-slate-900 dark:text-white">{file.name}</span> | |
| </div> | |
| </td> | |
| <td className="p-4"> | |
| <Badge className={`${getFileTypeColor(file.type)} border text-xs`}> | |
| {file.type} | |
| </Badge> | |
| </td> | |
| <td className="p-4 text-slate-600 dark:text-slate-400 font-mono text-sm"> | |
| {file.size} | |
| </td> | |
| <td className="p-4 text-slate-600 dark:text-slate-400 text-sm"> | |
| {file.modified} | |
| </td> | |
| <td className="p-4"> | |
| <span className="font-mono text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded text-slate-700 dark:text-slate-300"> | |
| {file.permissions} | |
| </span> | |
| </td> | |
| <td className="p-4"> | |
| <div className="flex items-center justify-end gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Edit3 className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Download className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| </Card> | |
| {/* Storage Overview */} | |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <div className="w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/20 flex items-center justify-center"> | |
| <Archive className="w-5 h-5 text-blue-600" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">Total Storage</h3> | |
| <p className="text-sm text-slate-500">500 GB available</p> | |
| </div> | |
| </div> | |
| <div className="space-y-2"> | |
| <div className="flex justify-between text-sm"> | |
| <span className="text-slate-600 dark:text-slate-400">Used</span> | |
| <span className="font-medium text-slate-900 dark:text-white">225 GB (45%)</span> | |
| </div> | |
| <div className="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2"> | |
| <div className="h-2 rounded-full bg-gradient-to-r from-blue-500 to-blue-600" style={{ width: '45%' }} /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <div className="w-10 h-10 rounded-lg bg-green-100 dark:bg-green-900/20 flex items-center justify-center"> | |
| <FileText className="w-5 h-5 text-green-600" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">Total Files</h3> | |
| <p className="text-sm text-slate-500">Across all directories</p> | |
| </div> | |
| </div> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">15,847</p> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <div className="w-10 h-10 rounded-lg bg-purple-100 dark:bg-purple-900/20 flex items-center justify-center"> | |
| <Upload className="w-5 h-5 text-purple-600" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">Recent Uploads</h3> | |
| <p className="text-sm text-slate-500">Last 24 hours</p> | |
| </div> | |
| </div> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">23</p> | |
| </Card> | |
| </div> | |
| </div> | |
| ); | |
| } |
| import { | |
| Activity, | |
| Globe, | |
| Users, | |
| Database, | |
| Cpu, | |
| HardDrive, | |
| Wifi, | |
| TrendingUp, | |
| AlertTriangle, | |
| CheckCircle | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| export function Dashboard() { | |
| const stats = [ | |
| { | |
| title: 'Active Sites', | |
| value: '12', | |
| change: '+2', | |
| icon: Globe, | |
| color: 'from-blue-500 to-blue-600', | |
| bgColor: 'bg-blue-50 dark:bg-blue-900/20' | |
| }, | |
| { | |
| title: 'Total Users', | |
| value: '1,247', | |
| change: '+18', | |
| icon: Users, | |
| color: 'from-green-500 to-green-600', | |
| bgColor: 'bg-green-50 dark:bg-green-900/20' | |
| }, | |
| { | |
| title: 'Database Size', | |
| value: '2.4GB', | |
| change: '+12MB', | |
| icon: Database, | |
| color: 'from-purple-500 to-purple-600', | |
| bgColor: 'bg-purple-50 dark:bg-purple-900/20' | |
| }, | |
| { | |
| title: 'Monthly Traffic', | |
| value: '45.2K', | |
| change: '+8.1%', | |
| icon: Activity, | |
| color: 'from-orange-500 to-red-500', | |
| bgColor: 'bg-orange-50 dark:bg-orange-900/20' | |
| }, | |
| ]; | |
| const systemStats = [ | |
| { label: 'CPU Usage', value: 24, color: 'bg-green-500', icon: Cpu }, | |
| { label: 'Memory Usage', value: 68, color: 'bg-yellow-500', icon: Activity }, | |
| { label: 'Disk Usage', value: 45, color: 'bg-blue-500', icon: HardDrive }, | |
| { label: 'Network I/O', value: 32, color: 'bg-purple-500', icon: Wifi }, | |
| ]; | |
| const recentActivity = [ | |
| { type: 'success', message: 'New site deployed: portfolio.dev', time: '2 minutes ago', icon: CheckCircle }, | |
| { type: 'warning', message: 'High memory usage detected', time: '15 minutes ago', icon: AlertTriangle }, | |
| { type: 'info', message: 'User john@example.com logged in', time: '1 hour ago', icon: Users }, | |
| { type: 'success', message: 'Database backup completed', time: '2 hours ago', icon: Database }, | |
| ]; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Dashboard</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Monitor your server performance and manage resources</p> | |
| </div> | |
| <div className="text-right"> | |
| <p className="text-sm text-slate-500">Last updated</p> | |
| <p className="font-medium text-slate-900 dark:text-white">2 minutes ago</p> | |
| </div> | |
| </div> | |
| {/* Stats Grid */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> | |
| {stats.map((stat, index) => { | |
| const Icon = stat.icon; | |
| return ( | |
| <Card key={index} className={`p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 ${stat.bgColor}`}> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">{stat.title}</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{stat.value}</p> | |
| <div className="flex items-center gap-1 mt-1"> | |
| <TrendingUp className="w-3 h-3 text-green-600" /> | |
| <span className="text-sm text-green-600">{stat.change}</span> | |
| </div> | |
| </div> | |
| <div className={`w-12 h-12 rounded-xl bg-gradient-to-r ${stat.color} flex items-center justify-center shadow-lg`}> | |
| <Icon className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| ); | |
| })} | |
| </div> | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |
| {/* System Performance */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-6">System Performance</h3> | |
| <div className="space-y-4"> | |
| {systemStats.map((stat, index) => { | |
| const Icon = stat.icon; | |
| return ( | |
| <div key={index} className="flex items-center gap-4"> | |
| <div className="w-10 h-10 rounded-lg bg-slate-100 dark:bg-slate-800 flex items-center justify-center"> | |
| <Icon className="w-5 h-5 text-slate-600 dark:text-slate-400" /> | |
| </div> | |
| <div className="flex-1"> | |
| <div className="flex justify-between items-center mb-1"> | |
| <span className="text-sm font-medium text-slate-700 dark:text-slate-300">{stat.label}</span> | |
| <span className="text-sm text-slate-600 dark:text-slate-400">{stat.value}%</span> | |
| </div> | |
| <div className="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2"> | |
| <div | |
| className={`h-2 rounded-full ${stat.color} transition-all duration-500`} | |
| style={{ width: `${stat.value}%` }} | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </Card> | |
| {/* Recent Activity */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-6">Recent Activity</h3> | |
| <div className="space-y-4"> | |
| {recentActivity.map((activity, index) => { | |
| const Icon = activity.icon; | |
| const colorMap = { | |
| success: 'text-green-600 bg-green-100 dark:bg-green-900/20', | |
| warning: 'text-yellow-600 bg-yellow-100 dark:bg-yellow-900/20', | |
| info: 'text-blue-600 bg-blue-100 dark:bg-blue-900/20' | |
| }; | |
| return ( | |
| <div key={index} className="flex items-start gap-3 p-3 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <div className={`w-8 h-8 rounded-full flex items-center justify-center ${colorMap[activity.type as keyof typeof colorMap]}`}> | |
| <Icon className="w-4 h-4" /> | |
| </div> | |
| <div className="flex-1"> | |
| <p className="text-sm text-slate-900 dark:text-white">{activity.message}</p> | |
| <p className="text-xs text-slate-500 mt-1">{activity.time}</p> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </Card> | |
| </div> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Link, | |
| Plus, | |
| ExternalLink, | |
| Settings, | |
| Trash2, | |
| Calendar, | |
| Shield, | |
| Eye, | |
| Key, | |
| Globe, | |
| Clock, | |
| CheckCircle, | |
| AlertTriangle, | |
| Wifi | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| import { ImageWithFallback } from './figma/ImageWithFallback'; | |
| export function DomainsManager() { | |
| const [domains] = useState([ | |
| { | |
| id: 1, | |
| domain: 'portfolio.dev', | |
| provider: 'Namecheap', | |
| registrar: 'Namecheap', | |
| loginUrl: 'https://ap.www.namecheap.com/myaccount', | |
| username: 'user@example.com', | |
| status: 'active', | |
| expiryDate: '2024-12-15', | |
| autoRenew: true, | |
| daysUntilExpiry: 138, | |
| dnsPrimary: 'ns1.digitalocean.com', | |
| dnsSecondary: 'ns2.digitalocean.com', | |
| sslStatus: 'active', | |
| sitePreview: 'https://via.placeholder.com/400x300/4F46E5/FFFFFF?text=Portfolio+Site' | |
| }, | |
| { | |
| id: 2, | |
| domain: 'example.com', | |
| provider: 'GoDaddy', | |
| registrar: 'GoDaddy', | |
| loginUrl: 'https://account.godaddy.com', | |
| username: 'admin@example.com', | |
| status: 'active', | |
| expiryDate: '2025-03-22', | |
| autoRenew: false, | |
| daysUntilExpiry: 235, | |
| dnsPrimary: 'ns1.godaddy.com', | |
| dnsSecondary: 'ns2.godaddy.com', | |
| sslStatus: 'expiring', | |
| sitePreview: 'https://via.placeholder.com/400x300/10B981/FFFFFF?text=E-commerce+Store' | |
| }, | |
| { | |
| id: 3, | |
| domain: 'blog.dev', | |
| provider: 'Cloudflare', | |
| registrar: 'Cloudflare', | |
| loginUrl: 'https://dash.cloudflare.com', | |
| username: 'blogger@example.com', | |
| status: 'inactive', | |
| expiryDate: '2024-08-10', | |
| autoRenew: true, | |
| daysUntilExpiry: 12, | |
| dnsPrimary: 'ns1.cloudflare.com', | |
| dnsSecondary: 'ns2.cloudflare.com', | |
| sslStatus: 'expired', | |
| sitePreview: 'https://via.placeholder.com/400x300/EF4444/FFFFFF?text=Blog+Platform' | |
| }, | |
| { | |
| id: 4, | |
| domain: 'api.service.com', | |
| provider: 'Route53', | |
| registrar: 'AWS', | |
| loginUrl: 'https://console.aws.amazon.com', | |
| username: 'root@company.com', | |
| status: 'active', | |
| expiryDate: '2025-01-30', | |
| autoRenew: true, | |
| daysUntilExpiry: 183, | |
| dnsPrimary: 'ns-1234.awsdns-56.com', | |
| dnsSecondary: 'ns-5678.awsdns-90.net', | |
| sslStatus: 'active', | |
| sitePreview: 'https://via.placeholder.com/400x300/8B5CF6/FFFFFF?text=API+Service' | |
| } | |
| ]); | |
| const getStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'active': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'inactive': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| case 'pending': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getSSLColor = (status: string) => { | |
| switch (status) { | |
| case 'active': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'expiring': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| case 'expired': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getExpiryUrgency = (days: number) => { | |
| if (days <= 30) return 'text-red-600'; | |
| if (days <= 90) return 'text-yellow-600'; | |
| return 'text-green-600'; | |
| }; | |
| const totalStats = { | |
| totalDomains: domains.length, | |
| activeDomains: domains.filter(d => d.status === 'active').length, | |
| expiringDomains: domains.filter(d => d.daysUntilExpiry <= 30).length, | |
| sslActive: domains.filter(d => d.sslStatus === 'active').length | |
| }; | |
| const providers = [ | |
| { name: 'Namecheap', count: 1, color: 'from-orange-500 to-orange-600' }, | |
| { name: 'GoDaddy', count: 1, color: 'from-green-500 to-green-600' }, | |
| { name: 'Cloudflare', count: 1, color: 'from-blue-500 to-blue-600' }, | |
| { name: 'AWS Route53', count: 1, color: 'from-purple-500 to-purple-600' } | |
| ]; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Domain Management</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage your domains, providers, and SSL certificates</p> | |
| </div> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| Add Domain | |
| </Button> | |
| </div> | |
| {/* Domain Statistics */} | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Total Domains</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.totalDomains}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center shadow-lg"> | |
| <Globe className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Active Domains</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.activeDomains}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-green-500 to-green-600 flex items-center justify-center shadow-lg"> | |
| <CheckCircle className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Expiring Soon</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.expiringDomains}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-yellow-500 to-yellow-600 flex items-center justify-center shadow-lg"> | |
| <AlertTriangle className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">SSL Active</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.sslActive}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Shield className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| </div> | |
| {/* Domains Grid */} | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {domains.map((domain) => ( | |
| <Card key={domain.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Link className="w-6 h-6 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{domain.domain}</h3> | |
| <p className="text-sm text-slate-500">{domain.provider}</p> | |
| </div> | |
| </div> | |
| <div className="flex flex-col gap-2"> | |
| <Badge className={`${getStatusColor(domain.status)} border`}> | |
| {domain.status} | |
| </Badge> | |
| <Badge className={`${getSSLColor(domain.sslStatus)} border`}> | |
| SSL: {domain.sslStatus} | |
| </Badge> | |
| </div> | |
| </div> | |
| {/* Site Preview */} | |
| <div className="mb-4"> | |
| <div className="relative w-full h-32 rounded-lg overflow-hidden bg-slate-100 dark:bg-slate-800"> | |
| <ImageWithFallback | |
| src={domain.sitePreview} | |
| alt={`Preview of ${domain.domain}`} | |
| className="w-full h-full object-cover" | |
| /> | |
| <div className="absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity"> | |
| <Button size="sm" variant="secondary" className="bg-white/90 text-slate-900"> | |
| <Eye className="w-3 h-3 mr-1" /> | |
| View Site | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Expires</p> | |
| <div className="flex items-center gap-1"> | |
| <Calendar className="w-3 h-3 text-slate-400" /> | |
| <span className="text-sm text-slate-700 dark:text-slate-300">{domain.expiryDate}</span> | |
| </div> | |
| <p className={`text-xs ${getExpiryUrgency(domain.daysUntilExpiry)}`}> | |
| {domain.daysUntilExpiry} days left | |
| </p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Auto Renew</p> | |
| <div className="flex items-center gap-1"> | |
| <Clock className="w-3 h-3 text-slate-400" /> | |
| <span className="text-sm text-slate-700 dark:text-slate-300"> | |
| {domain.autoRenew ? 'Enabled' : 'Disabled'} | |
| </span> | |
| </div> | |
| </div> | |
| <div className="col-span-2"> | |
| <p className="text-xs text-slate-500 mb-1">DNS Servers</p> | |
| <div className="space-y-1"> | |
| <div className="flex items-center gap-1"> | |
| <Wifi className="w-3 h-3 text-slate-400" /> | |
| <span className="text-xs text-slate-600 dark:text-slate-400 font-mono">{domain.dnsPrimary}</span> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Wifi className="w-3 h-3 text-slate-400" /> | |
| <span className="text-xs text-slate-600 dark:text-slate-400 font-mono">{domain.dnsSecondary}</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="mb-4 p-3 bg-slate-50 dark:bg-slate-800 rounded-lg"> | |
| <p className="text-xs text-slate-500 mb-1">Provider Login</p> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm font-medium text-slate-700 dark:text-slate-300">{domain.username}</p> | |
| <p className="text-xs text-slate-500 truncate">{domain.loginUrl}</p> | |
| </div> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Key className="w-3 h-3 mr-1" /> | |
| Login | |
| </Button> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <ExternalLink className="w-3 h-3 mr-1" /> | |
| Visit | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Wifi className="w-3 h-3 mr-1" /> | |
| DNS | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| {/* Provider Summary */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-6">Providers Overview</h3> | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> | |
| {providers.map((provider, index) => ( | |
| <div key={index} className="text-center p-4 bg-slate-50 dark:bg-slate-800 rounded-lg"> | |
| <div className={`w-12 h-12 rounded-lg bg-gradient-to-r ${provider.color} mx-auto mb-3 flex items-center justify-center shadow-lg`}> | |
| <Globe className="w-6 h-6 text-white" /> | |
| </div> | |
| <h4 className="font-medium text-slate-900 dark:text-white mb-1">{provider.name}</h4> | |
| <p className="text-sm text-slate-600 dark:text-slate-400">{provider.count} domain{provider.count !== 1 ? 's' : ''}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </Card> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { Sidebar } from './components/Sidebar'; | |
| import { Dashboard } from './components/Dashboard'; | |
| import { AppsManager } from './components/AppsManager'; | |
| import { DomainsManager } from './components/DomainsManager'; | |
| import { ServerManager } from './components/ServerManager'; | |
| import { NodeManager } from './components/NodeManager'; | |
| import { ContentManager } from './components/ContentManager'; | |
| import { UserManager } from './components/UserManager'; | |
| import { CodespaceManager } from './components/CodespaceManager'; | |
| import { GitHubManager } from './components/GitHubManager'; | |
| import { VSCodeManager } from './components/VSCodeManager'; | |
| export default function App() { | |
| const [activeSection, setActiveSection] = useState('dashboard'); | |
| const renderContent = () => { | |
| switch (activeSection) { | |
| case 'dashboard': | |
| return <Dashboard />; | |
| case 'apps': | |
| return <AppsManager />; | |
| case 'domains': | |
| return <DomainsManager />; | |
| case 'server': | |
| return <ServerManager />; | |
| case 'nodejs': | |
| return <NodeManager />; | |
| case 'content': | |
| return <ContentManager />; | |
| case 'users': | |
| return <UserManager />; | |
| case 'codespace': | |
| return <CodespaceManager />; | |
| case 'github': | |
| return <GitHubManager />; | |
| case 'vscode': | |
| return <VSCodeManager />; | |
| default: | |
| return <Dashboard />; | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800"> | |
| <div className="flex"> | |
| <Sidebar | |
| activeSection={activeSection} | |
| onSectionChange={setActiveSection} | |
| /> | |
| <main className="flex-1 ml-64"> | |
| <div className="p-8"> | |
| {renderContent()} | |
| </div> | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| }import { useState } from 'react'; | |
| import { | |
| Github, | |
| GitBranch, | |
| GitCommit, | |
| GitPullRequest, | |
| Star, | |
| Eye, | |
| Users, | |
| Calendar, | |
| ExternalLink, | |
| Plus, | |
| Zap | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| export function GitHubManager() { | |
| const [repositories] = useState([ | |
| { | |
| id: 1, | |
| name: 'portfolio-website', | |
| description: 'Modern portfolio website built with Next.js', | |
| visibility: 'public', | |
| language: 'TypeScript', | |
| stars: 24, | |
| forks: 8, | |
| lastCommit: '2 hours ago', | |
| branches: 3, | |
| pullRequests: 2, | |
| issues: 1 | |
| }, | |
| { | |
| id: 2, | |
| name: 'api-server', | |
| description: 'RESTful API server with Node.js and Express', | |
| visibility: 'private', | |
| language: 'JavaScript', | |
| stars: 12, | |
| forks: 3, | |
| lastCommit: '1 day ago', | |
| branches: 5, | |
| pullRequests: 0, | |
| issues: 3 | |
| }, | |
| { | |
| id: 3, | |
| name: 'blog-platform', | |
| description: 'Full-stack blog platform with React and MongoDB', | |
| visibility: 'public', | |
| language: 'JavaScript', | |
| stars: 67, | |
| forks: 23, | |
| lastCommit: '3 days ago', | |
| branches: 8, | |
| pullRequests: 4, | |
| issues: 7 | |
| }, | |
| { | |
| id: 4, | |
| name: 'mobile-app', | |
| description: 'React Native mobile application', | |
| visibility: 'private', | |
| language: 'TypeScript', | |
| stars: 5, | |
| forks: 1, | |
| lastCommit: '1 week ago', | |
| branches: 2, | |
| pullRequests: 1, | |
| issues: 0 | |
| } | |
| ]); | |
| const getLanguageColor = (language: string) => { | |
| switch (language) { | |
| case 'TypeScript': | |
| return 'bg-blue-500'; | |
| case 'JavaScript': | |
| return 'bg-yellow-500'; | |
| case 'Python': | |
| return 'bg-green-500'; | |
| case 'Go': | |
| return 'bg-cyan-500'; | |
| default: | |
| return 'bg-gray-500'; | |
| } | |
| }; | |
| const getVisibilityColor = (visibility: string) => { | |
| return visibility === 'public' | |
| ? 'bg-green-100 text-green-800 border-green-200' | |
| : 'bg-orange-100 text-orange-800 border-orange-200'; | |
| }; | |
| const totalStats = { | |
| repos: repositories.length, | |
| stars: repositories.reduce((acc, repo) => acc + repo.stars, 0), | |
| forks: repositories.reduce((acc, repo) => acc + repo.forks, 0), | |
| prs: repositories.reduce((acc, repo) => acc + repo.pullRequests, 0) | |
| }; | |
| const recentActivity = [ | |
| { type: 'commit', repo: 'portfolio-website', message: 'Update homepage design', time: '2 hours ago' }, | |
| { type: 'pr', repo: 'blog-platform', message: 'Add user authentication', time: '1 day ago' }, | |
| { type: 'issue', repo: 'api-server', message: 'Fix database connection timeout', time: '2 days ago' }, | |
| { type: 'star', repo: 'blog-platform', message: 'Received 3 new stars', time: '3 days ago' }, | |
| ]; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">GitHub Integration</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage your repositories and GitHub workflow</p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="outline"> | |
| <ExternalLink className="w-4 h-4 mr-2" /> | |
| Open GitHub | |
| </Button> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| New Repository | |
| </Button> | |
| </div> | |
| </div> | |
| {/* GitHub Stats */} | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Repositories</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.repos}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-gray-700 to-gray-800 flex items-center justify-center shadow-lg"> | |
| <Github className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Total Stars</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.stars}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-yellow-500 to-yellow-600 flex items-center justify-center shadow-lg"> | |
| <Star className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Total Forks</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.forks}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-green-500 to-green-600 flex items-center justify-center shadow-lg"> | |
| <GitBranch className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Open PRs</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{totalStats.prs}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <GitPullRequest className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| </div> | |
| {/* Repositories Grid */} | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {repositories.map((repo) => ( | |
| <Card key={repo.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-gray-700 to-gray-800 flex items-center justify-center shadow-lg"> | |
| <Github className="w-6 h-6 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{repo.name}</h3> | |
| <p className="text-sm text-slate-500 line-clamp-1">{repo.description}</p> | |
| </div> | |
| </div> | |
| <Badge className={`${getVisibilityColor(repo.visibility)} border`}> | |
| {repo.visibility} | |
| </Badge> | |
| </div> | |
| <div className="flex items-center gap-4 mb-4"> | |
| <div className="flex items-center gap-1"> | |
| <div className={`w-3 h-3 rounded-full ${getLanguageColor(repo.language)}`} /> | |
| <span className="text-sm text-slate-600 dark:text-slate-400">{repo.language}</span> | |
| </div> | |
| <div className="flex items-center gap-1 text-sm text-slate-600 dark:text-slate-400"> | |
| <Star className="w-3 h-3" /> | |
| {repo.stars} | |
| </div> | |
| <div className="flex items-center gap-1 text-sm text-slate-600 dark:text-slate-400"> | |
| <GitBranch className="w-3 h-3" /> | |
| {repo.forks} | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-3 gap-3 mb-4"> | |
| <div className="text-center p-2 bg-slate-50 dark:bg-slate-800 rounded-lg"> | |
| <p className="text-sm font-medium text-slate-900 dark:text-white">{repo.branches}</p> | |
| <p className="text-xs text-slate-500">Branches</p> | |
| </div> | |
| <div className="text-center p-2 bg-slate-50 dark:bg-slate-800 rounded-lg"> | |
| <p className="text-sm font-medium text-slate-900 dark:text-white">{repo.pullRequests}</p> | |
| <p className="text-xs text-slate-500">PRs</p> | |
| </div> | |
| <div className="text-center p-2 bg-slate-50 dark:bg-slate-800 rounded-lg"> | |
| <p className="text-sm font-medium text-slate-900 dark:text-white">{repo.issues}</p> | |
| <p className="text-xs text-slate-500">Issues</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-1 text-sm text-slate-500"> | |
| <GitCommit className="w-3 h-3" /> | |
| Last commit {repo.lastCommit} | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Eye className="w-3 h-3 mr-1" /> | |
| View | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <ExternalLink className="w-3 h-3 mr-1" /> | |
| Open | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| {/* Recent Activity */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-6">Recent Activity</h3> | |
| <div className="space-y-4"> | |
| {recentActivity.map((activity, index) => { | |
| const getActivityIcon = (type: string) => { | |
| switch (type) { | |
| case 'commit': | |
| return <GitCommit className="w-4 h-4 text-green-600" />; | |
| case 'pr': | |
| return <GitPullRequest className="w-4 h-4 text-blue-600" />; | |
| case 'issue': | |
| return <Zap className="w-4 h-4 text-red-600" />; | |
| case 'star': | |
| return <Star className="w-4 h-4 text-yellow-600" />; | |
| default: | |
| return <Github className="w-4 h-4 text-slate-600" />; | |
| } | |
| }; | |
| return ( | |
| <div key={index} className="flex items-start gap-3 p-3 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <div className="w-8 h-8 rounded-full bg-slate-100 dark:bg-slate-800 flex items-center justify-center mt-1"> | |
| {getActivityIcon(activity.type)} | |
| </div> | |
| <div className="flex-1"> | |
| <p className="text-sm text-slate-900 dark:text-white"> | |
| <span className="font-medium">{activity.repo}</span> • {activity.message} | |
| </p> | |
| <div className="flex items-center gap-1 mt-1"> | |
| <Calendar className="w-3 h-3 text-slate-400" /> | |
| <p className="text-xs text-slate-500">{activity.time}</p> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </Card> | |
| </div> | |
| ); | |
| } |
| @custom-variant dark (&:is(.dark *)); | |
| :root { | |
| --font-size: 14px; | |
| --background: #ffffff; | |
| --foreground: oklch(0.145 0 0); | |
| --card: #ffffff; | |
| --card-foreground: oklch(0.145 0 0); | |
| --popover: oklch(1 0 0); | |
| --popover-foreground: oklch(0.145 0 0); | |
| --primary: #030213; | |
| --primary-foreground: oklch(1 0 0); | |
| --secondary: oklch(0.95 0.0058 264.53); | |
| --secondary-foreground: #030213; | |
| --muted: #ececf0; | |
| --muted-foreground: #717182; | |
| --accent: #e9ebef; | |
| --accent-foreground: #030213; | |
| --destructive: #d4183d; | |
| --destructive-foreground: #ffffff; | |
| --border: rgba(0, 0, 0, 0.1); | |
| --input: transparent; | |
| --input-background: #f3f3f5; | |
| --switch-background: #cbced4; | |
| --font-weight-medium: 500; | |
| --font-weight-normal: 400; | |
| --ring: oklch(0.708 0 0); | |
| --chart-1: oklch(0.646 0.222 41.116); | |
| --chart-2: oklch(0.6 0.118 184.704); | |
| --chart-3: oklch(0.398 0.07 227.392); | |
| --chart-4: oklch(0.828 0.189 84.429); | |
| --chart-5: oklch(0.769 0.188 70.08); | |
| --radius: 0.625rem; | |
| --sidebar: oklch(0.985 0 0); | |
| --sidebar-foreground: oklch(0.145 0 0); | |
| --sidebar-primary: #030213; | |
| --sidebar-primary-foreground: oklch(0.985 0 0); | |
| --sidebar-accent: oklch(0.97 0 0); | |
| --sidebar-accent-foreground: oklch(0.205 0 0); | |
| --sidebar-border: oklch(0.922 0 0); | |
| --sidebar-ring: oklch(0.708 0 0); | |
| } | |
| .dark { | |
| --background: oklch(0.145 0 0); | |
| --foreground: oklch(0.985 0 0); | |
| --card: oklch(0.145 0 0); | |
| --card-foreground: oklch(0.985 0 0); | |
| --popover: oklch(0.145 0 0); | |
| --popover-foreground: oklch(0.985 0 0); | |
| --primary: oklch(0.985 0 0); | |
| --primary-foreground: oklch(0.205 0 0); | |
| --secondary: oklch(0.269 0 0); | |
| --secondary-foreground: oklch(0.985 0 0); | |
| --muted: oklch(0.269 0 0); | |
| --muted-foreground: oklch(0.708 0 0); | |
| --accent: oklch(0.269 0 0); | |
| --accent-foreground: oklch(0.985 0 0); | |
| --destructive: oklch(0.396 0.141 25.723); | |
| --destructive-foreground: oklch(0.637 0.237 25.331); | |
| --border: oklch(0.269 0 0); | |
| --input: oklch(0.269 0 0); | |
| --ring: oklch(0.439 0 0); | |
| --font-weight-medium: 500; | |
| --font-weight-normal: 400; | |
| --chart-1: oklch(0.488 0.243 264.376); | |
| --chart-2: oklch(0.696 0.17 162.48); | |
| --chart-3: oklch(0.769 0.188 70.08); | |
| --chart-4: oklch(0.627 0.265 303.9); | |
| --chart-5: oklch(0.645 0.246 16.439); | |
| --sidebar: oklch(0.205 0 0); | |
| --sidebar-foreground: oklch(0.985 0 0); | |
| --sidebar-primary: oklch(0.488 0.243 264.376); | |
| --sidebar-primary-foreground: oklch(0.985 0 0); | |
| --sidebar-accent: oklch(0.269 0 0); | |
| --sidebar-accent-foreground: oklch(0.985 0 0); | |
| --sidebar-border: oklch(0.269 0 0); | |
| --sidebar-ring: oklch(0.439 0 0); | |
| } | |
| @theme inline { | |
| --color-background: var(--background); | |
| --color-foreground: var(--foreground); | |
| --color-card: var(--card); | |
| --color-card-foreground: var(--card-foreground); | |
| --color-popover: var(--popover); | |
| --color-popover-foreground: var(--popover-foreground); | |
| --color-primary: var(--primary); | |
| --color-primary-foreground: var(--primary-foreground); | |
| --color-secondary: var(--secondary); | |
| --color-secondary-foreground: var(--secondary-foreground); | |
| --color-muted: var(--muted); | |
| --color-muted-foreground: var(--muted-foreground); | |
| --color-accent: var(--accent); | |
| --color-accent-foreground: var(--accent-foreground); | |
| --color-destructive: var(--destructive); | |
| --color-destructive-foreground: var(--destructive-foreground); | |
| --color-border: var(--border); | |
| --color-input: var(--input); | |
| --color-input-background: var(--input-background); | |
| --color-switch-background: var(--switch-background); | |
| --color-ring: var(--ring); | |
| --color-chart-1: var(--chart-1); | |
| --color-chart-2: var(--chart-2); | |
| --color-chart-3: var(--chart-3); | |
| --color-chart-4: var(--chart-4); | |
| --color-chart-5: var(--chart-5); | |
| --radius-sm: calc(var(--radius) - 4px); | |
| --radius-md: calc(var(--radius) - 2px); | |
| --radius-lg: var(--radius); | |
| --radius-xl: calc(var(--radius) + 4px); | |
| --color-sidebar: var(--sidebar); | |
| --color-sidebar-foreground: var(--sidebar-foreground); | |
| --color-sidebar-primary: var(--sidebar-primary); | |
| --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); | |
| --color-sidebar-accent: var(--sidebar-accent); | |
| --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); | |
| --color-sidebar-border: var(--sidebar-border); | |
| --color-sidebar-ring: var(--sidebar-ring); | |
| } | |
| @layer base { | |
| * { | |
| @apply border-border outline-ring/50; | |
| } | |
| body { | |
| @apply bg-background text-foreground; | |
| } | |
| } | |
| /** | |
| * Base typography. This is not applied to elements which have an ancestor with a Tailwind text class. | |
| */ | |
| @layer base { | |
| :where(:not(:has([class*=" text-"]), :not(:has([class^="text-"])))) { | |
| h1 { | |
| font-size: var(--text-2xl); | |
| font-weight: var(--font-weight-medium); | |
| line-height: 1.5; | |
| } | |
| h2 { | |
| font-size: var(--text-xl); | |
| font-weight: var(--font-weight-medium); | |
| line-height: 1.5; | |
| } | |
| h3 { | |
| font-size: var(--text-lg); | |
| font-weight: var(--font-weight-medium); | |
| line-height: 1.5; | |
| } | |
| h4 { | |
| font-size: var(--text-base); | |
| font-weight: var(--font-weight-medium); | |
| line-height: 1.5; | |
| } | |
| p { | |
| font-size: var(--text-base); | |
| font-weight: var(--font-weight-normal); | |
| line-height: 1.5; | |
| } | |
| label { | |
| font-size: var(--text-base); | |
| font-weight: var(--font-weight-medium); | |
| line-height: 1.5; | |
| } | |
| button { | |
| font-size: var(--text-base); | |
| font-weight: var(--font-weight-medium); | |
| line-height: 1.5; | |
| } | |
| input { | |
| font-size: var(--text-base); | |
| font-weight: var(--font-weight-normal); | |
| line-height: 1.5; | |
| } | |
| } | |
| } | |
| html { | |
| font-size: var(--font-size); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Terminal, | |
| Plus, | |
| Play, | |
| Square, | |
| Settings, | |
| Trash2, | |
| Package, | |
| Cpu, | |
| Activity, | |
| Download, | |
| Upload, | |
| RefreshCw, | |
| Search, | |
| Eye, | |
| AlertTriangle, | |
| CheckCircle, | |
| Clock, | |
| Zap, | |
| FileText, | |
| Folder, | |
| Command | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| import { Input } from './ui/input'; | |
| import { Textarea } from './ui/textarea'; | |
| export function NodeManager() { | |
| const [terminalInput, setTerminalInput] = useState(''); | |
| const [terminalHistory] = useState([ | |
| { command: 'node --version', output: 'v18.17.0', timestamp: '14:32:15', type: 'success' }, | |
| { command: 'npm list -g --depth=0', output: 'npm@9.8.1\nexpress@4.18.2\npm2@8.5.5\nnodemon@3.0.1', timestamp: '14:31:42', type: 'info' }, | |
| { command: 'pm2 status', output: 'Process not found', timestamp: '14:30:18', type: 'error' }, | |
| { command: 'ls -la', output: 'total 24\ndrwxr-xr-x 6 user user 4096 Jul 30 14:29 .\ndrwxr-xr-x 3 user user 4096 Jul 30 14:29 ..\n-rw-r--r-- 1 user user 245 Jul 30 14:29 package.json', timestamp: '14:29:55', type: 'info' } | |
| ]); | |
| const [processes] = useState([ | |
| { | |
| id: 1, | |
| name: 'api-server', | |
| script: 'server.js', | |
| status: 'running', | |
| pid: 12345, | |
| cpu: '2.5%', | |
| memory: '45.2MB', | |
| uptime: '2h 34m', | |
| restarts: 0, | |
| port: 3000 | |
| }, | |
| { | |
| id: 2, | |
| name: 'worker-process', | |
| script: 'worker.js', | |
| status: 'running', | |
| pid: 12346, | |
| cpu: '0.8%', | |
| memory: '23.1MB', | |
| uptime: '1h 15m', | |
| restarts: 2, | |
| port: null | |
| }, | |
| { | |
| id: 3, | |
| name: 'background-job', | |
| script: 'job.js', | |
| status: 'stopped', | |
| pid: null, | |
| cpu: '0%', | |
| memory: '0MB', | |
| uptime: '0m', | |
| restarts: 5, | |
| port: null | |
| } | |
| ]); | |
| const [packages] = useState([ | |
| { name: 'express', version: '4.18.2', type: 'dependency', size: '2.3MB', lastUpdated: '2 weeks ago' }, | |
| { name: 'mongoose', version: '7.4.1', type: 'dependency', size: '8.7MB', lastUpdated: '1 week ago' }, | |
| { name: 'lodash', version: '4.17.21', type: 'dependency', size: '1.4MB', lastUpdated: '3 months ago' }, | |
| { name: 'nodemon', version: '3.0.1', type: 'devDependency', size: '2.8MB', lastUpdated: '1 month ago' }, | |
| { name: 'jest', version: '29.6.1', type: 'devDependency', size: '5.2MB', lastUpdated: '2 weeks ago' }, | |
| { name: 'pm2', version: '5.3.0', type: 'global', size: '12.1MB', lastUpdated: '3 weeks ago' } | |
| ]); | |
| const [environments] = useState([ | |
| { key: 'NODE_ENV', value: 'production', description: 'Application environment' }, | |
| { key: 'PORT', value: '3000', description: 'Server port' }, | |
| { key: 'DB_URL', value: 'mongodb://localhost:27017/app', description: 'Database connection string' }, | |
| { key: 'JWT_SECRET', value: '••••••••••••••••', description: 'JWT signing secret' }, | |
| { key: 'API_KEY', value: '••••••••••••••••', description: 'External API key' } | |
| ]); | |
| const getStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'running': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'stopped': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| case 'error': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getPackageTypeColor = (type: string) => { | |
| switch (type) { | |
| case 'dependency': | |
| return 'bg-blue-100 text-blue-800 border-blue-200'; | |
| case 'devDependency': | |
| return 'bg-purple-100 text-purple-800 border-purple-200'; | |
| case 'global': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getTerminalTypeColor = (type: string) => { | |
| switch (type) { | |
| case 'success': | |
| return 'text-green-600'; | |
| case 'error': | |
| return 'text-red-600'; | |
| case 'warning': | |
| return 'text-yellow-600'; | |
| default: | |
| return 'text-slate-600 dark:text-slate-400'; | |
| } | |
| }; | |
| const nodeStats = { | |
| version: 'v18.17.0', | |
| npmVersion: '9.8.1', | |
| runningProcesses: processes.filter(p => p.status === 'running').length, | |
| totalPackages: packages.length, | |
| memoryUsage: processes.reduce((acc, p) => acc + parseFloat(p.memory.replace('MB', '')), 0) | |
| }; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Node.js & CLI</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage Node.js processes, packages, and execute commands</p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="outline"> | |
| <RefreshCw className="w-4 h-4 mr-2" /> | |
| Refresh All | |
| </Button> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| New Process | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Node.js Statistics */} | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Node.js Version</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{nodeStats.version}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-green-500 to-green-600 flex items-center justify-center shadow-lg"> | |
| <Cpu className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Running Processes</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{nodeStats.runningProcesses}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center shadow-lg"> | |
| <Activity className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Packages</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{nodeStats.totalPackages}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Package className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Memory Usage</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{nodeStats.memoryUsage.toFixed(1)}MB</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-orange-500 to-red-500 flex items-center justify-center shadow-lg"> | |
| <Zap className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| </div> | |
| <div className="grid grid-cols-1 xl:grid-cols-2 gap-8"> | |
| {/* Terminal Interface */} | |
| <div className="space-y-6"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Interactive Terminal</h2> | |
| <Card className="border-0 shadow-lg overflow-hidden"> | |
| <div className="bg-slate-900 text-green-400 p-4"> | |
| <div className="flex items-center gap-2 mb-4"> | |
| <Terminal className="w-4 h-4" /> | |
| <span className="font-mono text-sm">user@server:~/projects$</span> | |
| </div> | |
| <div className="space-y-2 max-h-64 overflow-y-auto mb-4"> | |
| {terminalHistory.map((entry, index) => ( | |
| <div key={index} className="font-mono text-sm"> | |
| <div className="flex items-center gap-2"> | |
| <span className="text-blue-400">$</span> | |
| <span className="text-white">{entry.command}</span> | |
| <span className="text-slate-500 text-xs">{entry.timestamp}</span> | |
| </div> | |
| <div className={`ml-4 whitespace-pre-wrap ${getTerminalTypeColor(entry.type)}`}> | |
| {entry.output} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <span className="text-blue-400">$</span> | |
| <Input | |
| value={terminalInput} | |
| onChange={(e) => setTerminalInput(e.target.value)} | |
| placeholder="Enter command..." | |
| className="bg-transparent border-none text-white font-mono text-sm p-0 focus:ring-0" | |
| /> | |
| <Button size="sm" className="bg-green-600 hover:bg-green-700"> | |
| <Command className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| {/* Process Management */} | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Process Management</h2> | |
| <Button variant="outline" size="sm"> | |
| <Eye className="w-4 h-4 mr-2" /> | |
| Monitor | |
| </Button> | |
| </div> | |
| <div className="space-y-4"> | |
| {processes.map((process) => ( | |
| <Card key={process.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Activity className="w-6 h-6 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{process.name}</h3> | |
| <p className="text-sm text-slate-500 font-mono">{process.script}</p> | |
| </div> | |
| </div> | |
| <Badge className={`${getStatusColor(process.status)} border`}> | |
| {process.status} | |
| </Badge> | |
| </div> | |
| <div className="grid grid-cols-3 gap-4 mb-4"> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">PID</p> | |
| <p className="text-sm font-medium text-slate-700 dark:text-slate-300">{process.pid || 'N/A'}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">CPU</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{process.cpu}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Memory</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{process.memory}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Uptime</p> | |
| <div className="flex items-center gap-1"> | |
| <Clock className="w-3 h-3 text-slate-400" /> | |
| <span className="text-sm text-slate-700 dark:text-slate-300">{process.uptime}</span> | |
| </div> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Restarts</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{process.restarts}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Port</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{process.port || 'N/A'}</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <FileText className="w-3 h-3 mr-1" /> | |
| Logs | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <RefreshCw className="w-3 h-3 mr-1" /> | |
| Restart | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| {process.status === 'running' ? | |
| <Square className="w-3 h-3 text-red-600" /> : | |
| <Play className="w-3 h-3 text-green-600" /> | |
| } | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Package & Environment Management */} | |
| <div className="space-y-6"> | |
| {/* Package Management */} | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Package Management</h2> | |
| <Button variant="outline" size="sm"> | |
| <Download className="w-4 h-4 mr-2" /> | |
| Install | |
| </Button> | |
| </div> | |
| <Card className="border-0 shadow-lg"> | |
| <div className="p-4 border-b border-slate-200 dark:border-slate-700"> | |
| <div className="relative"> | |
| <Search className="w-4 h-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400" /> | |
| <Input placeholder="Search packages..." className="pl-10" /> | |
| </div> | |
| </div> | |
| <div className="max-h-64 overflow-y-auto"> | |
| {packages.map((pkg, index) => ( | |
| <div key={index} className="p-4 border-b border-slate-200 dark:border-slate-700 last:border-0 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-8 h-8 rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center"> | |
| <Package className="w-4 h-4 text-white" /> | |
| </div> | |
| <div> | |
| <div className="flex items-center gap-2"> | |
| <span className="font-medium text-slate-900 dark:text-white">{pkg.name}</span> | |
| <Badge className={`${getPackageTypeColor(pkg.type)} border text-xs`}> | |
| {pkg.type} | |
| </Badge> | |
| </div> | |
| <div className="flex items-center gap-4 text-xs text-slate-500"> | |
| <span>v{pkg.version}</span> | |
| <span>{pkg.size}</span> | |
| <span>{pkg.lastUpdated}</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Upload className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </Card> | |
| {/* Environment Variables */} | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Environment Variables</h2> | |
| <Button variant="outline" size="sm"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| Add Variable | |
| </Button> | |
| </div> | |
| <Card className="border-0 shadow-lg"> | |
| <div className="divide-y divide-slate-200 dark:divide-slate-700"> | |
| {environments.map((env, index) => ( | |
| <div key={index} className="p-4 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <div className="flex items-center gap-2"> | |
| <span className="font-mono font-medium text-slate-900 dark:text-white">{env.key}</span> | |
| {env.value.includes('••••') && ( | |
| <Badge className="bg-yellow-100 text-yellow-800 border-yellow-200 text-xs"> | |
| Secret | |
| </Badge> | |
| )} | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Eye className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| <p className="font-mono text-sm text-slate-600 dark:text-slate-400 mb-1">{env.value}</p> | |
| <p className="text-xs text-slate-500">{env.description}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </Card> | |
| {/* Quick Actions */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-4">Quick Commands</h3> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Package className="w-6 h-6" /> | |
| <span className="text-xs">npm install</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <RefreshCw className="w-6 h-6" /> | |
| <span className="text-xs">npm update</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Activity className="w-6 h-6" /> | |
| <span className="text-xs">pm2 status</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <FileText className="w-6 h-6" /> | |
| <span className="text-xs">View Logs</span> | |
| </Button> | |
| </div> | |
| </Card> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Database, | |
| Plus, | |
| Settings, | |
| Trash2, | |
| Wifi, | |
| Shield, | |
| Activity, | |
| Server, | |
| HardDrive, | |
| Network, | |
| Lock, | |
| Download, | |
| Upload, | |
| Play, | |
| Square, | |
| RefreshCw, | |
| Eye, | |
| AlertTriangle | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| import { Input } from './ui/input'; | |
| export function ServerManager() { | |
| const [dnsRecords] = useState([ | |
| { id: 1, type: 'A', name: '@', value: '192.168.1.100', ttl: 3600, status: 'active' }, | |
| { id: 2, type: 'A', name: 'www', value: '192.168.1.100', ttl: 3600, status: 'active' }, | |
| { id: 3, type: 'CNAME', name: 'api', value: 'api.service.com', ttl: 1800, status: 'active' }, | |
| { id: 4, type: 'MX', name: '@', value: 'mail.example.com', ttl: 7200, status: 'active' }, | |
| { id: 5, type: 'TXT', name: '@', value: 'v=spf1 include:_spf.google.com ~all', ttl: 3600, status: 'active' } | |
| ]); | |
| const [ports] = useState([ | |
| { id: 1, port: 22, protocol: 'TCP', service: 'SSH', status: 'open', description: 'Secure Shell access' }, | |
| { id: 2, port: 80, protocol: 'TCP', service: 'HTTP', status: 'open', description: 'Web server' }, | |
| { id: 3, port: 443, protocol: 'TCP', service: 'HTTPS', status: 'open', description: 'Secure web server' }, | |
| { id: 4, port: 3306, protocol: 'TCP', service: 'MySQL', status: 'filtered', description: 'MySQL database' }, | |
| { id: 5, port: 5432, protocol: 'TCP', service: 'PostgreSQL', status: 'closed', description: 'PostgreSQL database' }, | |
| { id: 6, port: 6379, protocol: 'TCP', service: 'Redis', status: 'open', description: 'Redis cache' } | |
| ]); | |
| const [databases] = useState([ | |
| { | |
| id: 1, | |
| name: 'portfolio.db', | |
| size: '2.4 MB', | |
| tables: 8, | |
| lastBackup: '2 hours ago', | |
| status: 'healthy', | |
| location: '/var/www/portfolio/data/portfolio.db' | |
| }, | |
| { | |
| id: 2, | |
| name: 'analytics.db', | |
| size: '15.7 MB', | |
| tables: 12, | |
| lastBackup: '1 day ago', | |
| status: 'healthy', | |
| location: '/var/www/analytics/data/analytics.db' | |
| }, | |
| { | |
| id: 3, | |
| name: 'users.db', | |
| size: '8.2 MB', | |
| tables: 6, | |
| lastBackup: '4 hours ago', | |
| status: 'warning', | |
| location: '/var/www/users/data/users.db' | |
| }, | |
| { | |
| id: 4, | |
| name: 'logs.db', | |
| size: '125 MB', | |
| tables: 3, | |
| lastBackup: '12 hours ago', | |
| status: 'healthy', | |
| location: '/var/logs/application/logs.db' | |
| } | |
| ]); | |
| const getRecordTypeColor = (type: string) => { | |
| switch (type) { | |
| case 'A': | |
| return 'bg-blue-100 text-blue-800 border-blue-200'; | |
| case 'CNAME': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'MX': | |
| return 'bg-purple-100 text-purple-800 border-purple-200'; | |
| case 'TXT': | |
| return 'bg-orange-100 text-orange-800 border-orange-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getPortStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'open': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'closed': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| case 'filtered': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getDBStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'healthy': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'warning': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| case 'error': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const serverStats = { | |
| totalDNSRecords: dnsRecords.length, | |
| openPorts: ports.filter(p => p.status === 'open').length, | |
| totalDatabases: databases.length, | |
| totalDBSize: databases.reduce((acc, db) => { | |
| const size = parseFloat(db.size); | |
| const unit = db.size.includes('MB') ? 1 : 0.001; | |
| return acc + (size * unit); | |
| }, 0) | |
| }; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">Server Configuration</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage DNS, ports, and SQLite databases</p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="outline"> | |
| <RefreshCw className="w-4 h-4 mr-2" /> | |
| Refresh Status | |
| </Button> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Settings className="w-4 h-4 mr-2" /> | |
| Configure | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Server Statistics */} | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">DNS Records</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{serverStats.totalDNSRecords}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center shadow-lg"> | |
| <Wifi className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Open Ports</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{serverStats.openPorts}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-green-500 to-green-600 flex items-center justify-center shadow-lg"> | |
| <Network className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Databases</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{serverStats.totalDatabases}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Database className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">DB Storage</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{serverStats.totalDBSize.toFixed(1)}MB</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-orange-500 to-red-500 flex items-center justify-center shadow-lg"> | |
| <HardDrive className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| </div> | |
| <div className="grid grid-cols-1 xl:grid-cols-2 gap-8"> | |
| {/* DNS Management */} | |
| <div className="space-y-6"> | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">DNS Records</h2> | |
| <Button variant="outline" size="sm"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| Add Record | |
| </Button> | |
| </div> | |
| <Card className="border-0 shadow-lg overflow-hidden"> | |
| <div className="overflow-x-auto"> | |
| <table className="w-full"> | |
| <thead className="bg-slate-50 dark:bg-slate-800"> | |
| <tr> | |
| <th className="text-left p-3 font-medium text-slate-700 dark:text-slate-300">Type</th> | |
| <th className="text-left p-3 font-medium text-slate-700 dark:text-slate-300">Name</th> | |
| <th className="text-left p-3 font-medium text-slate-700 dark:text-slate-300">Value</th> | |
| <th className="text-left p-3 font-medium text-slate-700 dark:text-slate-300">TTL</th> | |
| <th className="text-right p-3 font-medium text-slate-700 dark:text-slate-300">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {dnsRecords.map((record) => ( | |
| <tr key={record.id} className="border-t border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <td className="p-3"> | |
| <Badge className={`${getRecordTypeColor(record.type)} border text-xs`}> | |
| {record.type} | |
| </Badge> | |
| </td> | |
| <td className="p-3 font-mono text-sm text-slate-700 dark:text-slate-300">{record.name}</td> | |
| <td className="p-3 font-mono text-sm text-slate-600 dark:text-slate-400 max-w-48 truncate">{record.value}</td> | |
| <td className="p-3 text-sm text-slate-600 dark:text-slate-400">{record.ttl}s</td> | |
| <td className="p-3"> | |
| <div className="flex items-center justify-end gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| </Card> | |
| {/* Port Management */} | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Port Configuration</h2> | |
| <Button variant="outline" size="sm"> | |
| <Shield className="w-4 h-4 mr-2" /> | |
| Firewall | |
| </Button> | |
| </div> | |
| <div className="space-y-3"> | |
| {ports.map((port) => ( | |
| <Card key={port.id} className="p-4 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-4"> | |
| <div className="w-10 h-10 rounded-lg bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-center"> | |
| <Network className="w-5 h-5 text-white" /> | |
| </div> | |
| <div> | |
| <div className="flex items-center gap-2"> | |
| <span className="font-semibold text-slate-900 dark:text-white">{port.port}</span> | |
| <span className="text-sm text-slate-500">/{port.protocol}</span> | |
| <Badge className={`${getPortStatusColor(port.status)} border text-xs`}> | |
| {port.status} | |
| </Badge> | |
| </div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400">{port.service} - {port.description}</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Activity className="w-3 h-3 mr-1" /> | |
| Monitor | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Database Management */} | |
| <div className="space-y-6"> | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">SQLite Databases</h2> | |
| <Button variant="outline" size="sm"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| New Database | |
| </Button> | |
| </div> | |
| <div className="space-y-4"> | |
| {databases.map((db) => ( | |
| <Card key={db.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Database className="w-6 h-6 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{db.name}</h3> | |
| <p className="text-sm text-slate-500 font-mono truncate max-w-64">{db.location}</p> | |
| </div> | |
| </div> | |
| <Badge className={`${getDBStatusColor(db.status)} border`}> | |
| {db.status} | |
| </Badge> | |
| </div> | |
| <div className="grid grid-cols-3 gap-4 mb-4"> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Size</p> | |
| <p className="text-sm font-medium text-slate-700 dark:text-slate-300">{db.size}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Tables</p> | |
| <p className="text-sm font-medium text-slate-700 dark:text-slate-300">{db.tables}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Last Backup</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{db.lastBackup}</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Eye className="w-3 h-3 mr-1" /> | |
| Query | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Download className="w-3 h-3 mr-1" /> | |
| Backup | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Upload className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| {/* Quick Database Actions */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-4">Database Tools</h3> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Download className="w-6 h-6" /> | |
| <span className="text-xs">Backup All</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Upload className="w-6 h-6" /> | |
| <span className="text-xs">Restore</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Activity className="w-6 h-6" /> | |
| <span className="text-xs">Monitor</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <RefreshCw className="w-6 h-6" /> | |
| <span className="text-xs">Optimize</span> | |
| </Button> | |
| </div> | |
| </Card> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Users, | |
| Plus, | |
| Shield, | |
| Mail, | |
| Calendar, | |
| Edit3, | |
| Trash2, | |
| UserCheck, | |
| UserX, | |
| Crown, | |
| User | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| import { Avatar } from './ui/avatar'; | |
| export function UserManager() { | |
| const [users] = useState([ | |
| { | |
| id: 1, | |
| name: 'John Smith', | |
| email: 'john.smith@example.com', | |
| role: 'admin', | |
| status: 'active', | |
| lastLogin: '2 hours ago', | |
| created: '2023-01-15', | |
| avatar: 'JS' | |
| }, | |
| { | |
| id: 2, | |
| name: 'Sarah Johnson', | |
| email: 'sarah.j@example.com', | |
| role: 'editor', | |
| status: 'active', | |
| lastLogin: '1 day ago', | |
| created: '2023-03-22', | |
| avatar: 'SJ' | |
| }, | |
| { | |
| id: 3, | |
| name: 'Mike Wilson', | |
| email: 'mike.w@example.com', | |
| role: 'user', | |
| status: 'inactive', | |
| lastLogin: '1 week ago', | |
| created: '2023-06-10', | |
| avatar: 'MW' | |
| }, | |
| { | |
| id: 4, | |
| name: 'Emily Davis', | |
| email: 'emily.davis@example.com', | |
| role: 'editor', | |
| status: 'active', | |
| lastLogin: '3 hours ago', | |
| created: '2023-04-18', | |
| avatar: 'ED' | |
| }, | |
| { | |
| id: 5, | |
| name: 'David Brown', | |
| email: 'david.brown@example.com', | |
| role: 'user', | |
| status: 'active', | |
| lastLogin: '5 minutes ago', | |
| created: '2023-07-25', | |
| avatar: 'DB' | |
| } | |
| ]); | |
| const getRoleColor = (role: string) => { | |
| switch (role) { | |
| case 'admin': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| case 'editor': | |
| return 'bg-blue-100 text-blue-800 border-blue-200'; | |
| case 'user': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getStatusColor = (status: string) => { | |
| switch (status) { | |
| case 'active': | |
| return 'bg-green-100 text-green-800 border-green-200'; | |
| case 'inactive': | |
| return 'bg-yellow-100 text-yellow-800 border-yellow-200'; | |
| case 'suspended': | |
| return 'bg-red-100 text-red-800 border-red-200'; | |
| default: | |
| return 'bg-gray-100 text-gray-800 border-gray-200'; | |
| } | |
| }; | |
| const getRoleIcon = (role: string) => { | |
| switch (role) { | |
| case 'admin': | |
| return <Crown className="w-4 h-4" />; | |
| case 'editor': | |
| return <Shield className="w-4 h-4" />; | |
| case 'user': | |
| return <User className="w-4 h-4" />; | |
| default: | |
| return <User className="w-4 h-4" />; | |
| } | |
| }; | |
| const roleStats = [ | |
| { role: 'Admin', count: 1, color: 'from-red-500 to-red-600', icon: Crown }, | |
| { role: 'Editor', count: 2, color: 'from-blue-500 to-blue-600', icon: Shield }, | |
| { role: 'User', count: 2, color: 'from-green-500 to-green-600', icon: User }, | |
| ]; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">User Management</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage user accounts, roles, and permissions</p> | |
| </div> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Plus className="w-4 h-4 mr-2" /> | |
| Add New User | |
| </Button> | |
| </div> | |
| {/* Role Statistics */} | |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| {roleStats.map((stat, index) => { | |
| const Icon = stat.icon; | |
| return ( | |
| <Card key={index} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">{stat.role}s</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{stat.count}</p> | |
| </div> | |
| <div className={`w-12 h-12 rounded-xl bg-gradient-to-r ${stat.color} flex items-center justify-center shadow-lg`}> | |
| <Icon className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| ); | |
| })} | |
| </div> | |
| {/* Users List */} | |
| <Card className="border-0 shadow-lg"> | |
| <div className="p-6 border-b border-slate-200 dark:border-slate-700"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white">All Users</h3> | |
| </div> | |
| <div className="overflow-x-auto"> | |
| <table className="w-full"> | |
| <thead className="bg-slate-50 dark:bg-slate-800"> | |
| <tr> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">User</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Role</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Status</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Last Login</th> | |
| <th className="text-left p-4 font-medium text-slate-700 dark:text-slate-300">Created</th> | |
| <th className="text-right p-4 font-medium text-slate-700 dark:text-slate-300">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {users.map((user) => ( | |
| <tr key={user.id} className="border-t border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <td className="p-4"> | |
| <div className="flex items-center gap-3"> | |
| <Avatar className="w-10 h-10 bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center text-white font-medium"> | |
| {user.avatar} | |
| </Avatar> | |
| <div> | |
| <p className="font-medium text-slate-900 dark:text-white">{user.name}</p> | |
| <div className="flex items-center gap-1 text-sm text-slate-500"> | |
| <Mail className="w-3 h-3" /> | |
| {user.email} | |
| </div> | |
| </div> | |
| </div> | |
| </td> | |
| <td className="p-4"> | |
| <Badge className={`${getRoleColor(user.role)} border flex items-center gap-1 w-fit`}> | |
| {getRoleIcon(user.role)} | |
| {user.role} | |
| </Badge> | |
| </td> | |
| <td className="p-4"> | |
| <Badge className={`${getStatusColor(user.status)} border`}> | |
| {user.status} | |
| </Badge> | |
| </td> | |
| <td className="p-4 text-slate-600 dark:text-slate-400 text-sm"> | |
| {user.lastLogin} | |
| </td> | |
| <td className="p-4"> | |
| <div className="flex items-center gap-1 text-sm text-slate-600 dark:text-slate-400"> | |
| <Calendar className="w-3 h-3" /> | |
| {user.created} | |
| </div> | |
| </td> | |
| <td className="p-4"> | |
| <div className="flex items-center justify-end gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Edit3 className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| {user.status === 'active' ? | |
| <UserX className="w-3 h-3 text-yellow-600" /> : | |
| <UserCheck className="w-3 h-3 text-green-600" /> | |
| } | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"> | |
| <Trash2 className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| </Card> | |
| {/* Recent Activity */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-6">Recent User Activity</h3> | |
| <div className="space-y-4"> | |
| {[ | |
| { user: 'John Smith', action: 'Updated profile settings', time: '2 hours ago', type: 'update' }, | |
| { user: 'Sarah Johnson', action: 'Created new content', time: '4 hours ago', type: 'create' }, | |
| { user: 'David Brown', action: 'Logged in from new device', time: '6 hours ago', type: 'login' }, | |
| { user: 'Emily Davis', action: 'Changed password', time: '1 day ago', type: 'security' }, | |
| ].map((activity, index) => { | |
| const getActivityIcon = (type: string) => { | |
| switch (type) { | |
| case 'update': | |
| return <Edit3 className="w-4 h-4 text-blue-600" />; | |
| case 'create': | |
| return <Plus className="w-4 h-4 text-green-600" />; | |
| case 'login': | |
| return <UserCheck className="w-4 h-4 text-purple-600" />; | |
| case 'security': | |
| return <Shield className="w-4 h-4 text-orange-600" />; | |
| default: | |
| return <User className="w-4 h-4 text-slate-600" />; | |
| } | |
| }; | |
| return ( | |
| <div key={index} className="flex items-center gap-3 p-3 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors"> | |
| <div className="w-8 h-8 rounded-full bg-slate-100 dark:bg-slate-800 flex items-center justify-center"> | |
| {getActivityIcon(activity.type)} | |
| </div> | |
| <div className="flex-1"> | |
| <p className="text-sm text-slate-900 dark:text-white"> | |
| <span className="font-medium">{activity.user}</span> {activity.action} | |
| </p> | |
| <p className="text-xs text-slate-500 mt-1">{activity.time}</p> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </Card> | |
| </div> | |
| ); | |
| } |
| import { useState } from 'react'; | |
| import { | |
| Code, | |
| Play, | |
| Square, | |
| Settings, | |
| Puzzle, | |
| Search, | |
| FolderOpen, | |
| Terminal, | |
| GitBranch, | |
| Bug, | |
| Zap | |
| } from 'lucide-react'; | |
| import { Card } from './ui/card'; | |
| import { Button } from './ui/button'; | |
| import { Badge } from './ui/badge'; | |
| export function VSCodeManager() { | |
| const [workspaces] = useState([ | |
| { | |
| id: 1, | |
| name: 'Portfolio Project', | |
| path: '/workspace/portfolio', | |
| status: 'active', | |
| language: 'TypeScript', | |
| openFiles: 8, | |
| branch: 'main', | |
| lastOpened: '2 minutes ago' | |
| }, | |
| { | |
| id: 2, | |
| name: 'API Server', | |
| path: '/workspace/api-server', | |
| status: 'inactive', | |
| language: 'JavaScript', | |
| openFiles: 3, | |
| branch: 'develop', | |
| lastOpened: '2 hours ago' | |
| }, | |
| { | |
| id: 3, | |
| name: 'Mobile App', | |
| path: '/workspace/mobile-app', | |
| status: 'active', | |
| language: 'React Native', | |
| openFiles: 12, | |
| branch: 'feature/auth', | |
| lastOpened: '30 minutes ago' | |
| } | |
| ]); | |
| const [extensions] = useState([ | |
| { name: 'Prettier', description: 'Code formatter', version: '9.10.4', enabled: true }, | |
| { name: 'ESLint', description: 'JavaScript linter', version: '2.4.2', enabled: true }, | |
| { name: 'GitLens', description: 'Git supercharged', version: '13.6.0', enabled: true }, | |
| { name: 'Thunder Client', description: 'REST API testing', version: '2.8.0', enabled: false }, | |
| { name: 'Live Server', description: 'Local development server', version: '5.7.9', enabled: true }, | |
| ]); | |
| const getStatusColor = (status: string) => { | |
| return status === 'active' | |
| ? 'bg-green-100 text-green-800 border-green-200' | |
| : 'bg-gray-100 text-gray-800 border-gray-200'; | |
| }; | |
| const getLanguageColor = (language: string) => { | |
| switch (language) { | |
| case 'TypeScript': | |
| return 'bg-blue-500'; | |
| case 'JavaScript': | |
| return 'bg-yellow-500'; | |
| case 'React Native': | |
| return 'bg-cyan-500'; | |
| default: | |
| return 'bg-gray-500'; | |
| } | |
| }; | |
| return ( | |
| <div className="space-y-8"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-3xl font-bold text-slate-900 dark:text-white">VS Code Web</h1> | |
| <p className="text-slate-600 dark:text-slate-400">Manage your VS Code workspaces and settings</p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="outline"> | |
| <FolderOpen className="w-4 h-4 mr-2" /> | |
| Open Folder | |
| </Button> | |
| <Button className="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg"> | |
| <Code className="w-4 h-4 mr-2" /> | |
| New Workspace | |
| </Button> | |
| </div> | |
| </div> | |
| {/* Quick Stats */} | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-6"> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Active Workspaces</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white"> | |
| {workspaces.filter(w => w.status === 'active').length} | |
| </p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center shadow-lg"> | |
| <Code className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Open Files</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white"> | |
| {workspaces.reduce((acc, w) => acc + w.openFiles, 0)} | |
| </p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-green-500 to-green-600 flex items-center justify-center shadow-lg"> | |
| <FolderOpen className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Extensions</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white">{extensions.length}</p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-purple-600 flex items-center justify-center shadow-lg"> | |
| <Puzzle className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="text-sm text-slate-600 dark:text-slate-400 mb-1">Enabled</p> | |
| <p className="text-2xl font-bold text-slate-900 dark:text-white"> | |
| {extensions.filter(e => e.enabled).length} | |
| </p> | |
| </div> | |
| <div className="w-12 h-12 rounded-xl bg-gradient-to-r from-orange-500 to-red-500 flex items-center justify-center shadow-lg"> | |
| <Zap className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| </Card> | |
| </div> | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> | |
| {/* Workspaces */} | |
| <div className="space-y-6"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Workspaces</h2> | |
| <div className="space-y-4"> | |
| {workspaces.map((workspace) => ( | |
| <Card key={workspace.id} className="p-6 border-0 shadow-lg hover:shadow-xl transition-all duration-300"> | |
| <div className="flex items-start justify-between mb-4"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-10 h-10 rounded-lg bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-center"> | |
| <Code className="w-5 h-5 text-white" /> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-slate-900 dark:text-white">{workspace.name}</h3> | |
| <p className="text-sm text-slate-500 font-mono">{workspace.path}</p> | |
| </div> | |
| </div> | |
| <Badge className={`${getStatusColor(workspace.status)} border`}> | |
| {workspace.status} | |
| </Badge> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4 mb-4"> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Language</p> | |
| <div className="flex items-center gap-1"> | |
| <div className={`w-3 h-3 rounded-full ${getLanguageColor(workspace.language)}`} /> | |
| <span className="text-sm text-slate-700 dark:text-slate-300">{workspace.language}</span> | |
| </div> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Open Files</p> | |
| <p className="text-sm font-medium text-slate-700 dark:text-slate-300">{workspace.openFiles}</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Branch</p> | |
| <div className="flex items-center gap-1"> | |
| <GitBranch className="w-3 h-3 text-slate-400" /> | |
| <span className="text-sm text-slate-700 dark:text-slate-300">{workspace.branch}</span> | |
| </div> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-500 mb-1">Last Opened</p> | |
| <p className="text-sm text-slate-700 dark:text-slate-300">{workspace.lastOpened}</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between pt-4 border-t border-slate-200 dark:border-slate-700"> | |
| <div className="flex items-center gap-2"> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Code className="w-3 h-3 mr-1" /> | |
| Open | |
| </Button> | |
| <Button size="sm" variant="outline" className="h-8"> | |
| <Terminal className="w-3 h-3 mr-1" /> | |
| Terminal | |
| </Button> | |
| </div> | |
| <div className="flex items-center gap-1"> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Bug className="w-3 h-3" /> | |
| </Button> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Extensions */} | |
| <div className="space-y-6"> | |
| <div className="flex items-center justify-between"> | |
| <h2 className="text-xl font-semibold text-slate-900 dark:text-white">Extensions</h2> | |
| <Button variant="outline" size="sm"> | |
| <Search className="w-4 h-4 mr-2" /> | |
| Browse | |
| </Button> | |
| </div> | |
| <div className="space-y-4"> | |
| {extensions.map((extension, index) => ( | |
| <Card key={index} className="p-4 border-0 shadow-lg"> | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-10 h-10 rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center"> | |
| <Puzzle className="w-5 h-5 text-white" /> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-slate-900 dark:text-white">{extension.name}</h4> | |
| <p className="text-sm text-slate-500">{extension.description}</p> | |
| <p className="text-xs text-slate-400">v{extension.version}</p> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <Badge className={extension.enabled | |
| ? 'bg-green-100 text-green-800 border-green-200' | |
| : 'bg-gray-100 text-gray-800 border-gray-200'}> | |
| {extension.enabled ? 'Enabled' : 'Disabled'} | |
| </Badge> | |
| <Button size="sm" variant="ghost" className="h-8 w-8 p-0"> | |
| <Settings className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| {/* Quick Actions */} | |
| <Card className="p-6 border-0 shadow-lg"> | |
| <h3 className="text-lg font-semibold text-slate-900 dark:text-white mb-4">Quick Actions</h3> | |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Terminal className="w-6 h-6" /> | |
| <span className="text-xs">Integrated Terminal</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Search className="w-6 h-6" /> | |
| <span className="text-xs">Command Palette</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <GitBranch className="w-6 h-6" /> | |
| <span className="text-xs">Source Control</span> | |
| </Button> | |
| <Button variant="outline" className="h-16 flex-col gap-2"> | |
| <Settings className="w-6 h-6" /> | |
| <span className="text-xs">Settings</span> | |
| </Button> | |
| </div> | |
| </Card> | |
| </div> | |
| ); | |
| } |