Skip to content

Instantly share code, notes, and snippets.

@mhekel
Last active August 2, 2025 08:57
Show Gist options
  • Select an option

  • Save mhekel/98aa336c93d5d0707ce3d3e686b512d7 to your computer and use it in GitHub Desktop.

Select an option

Save mhekel/98aa336c93d5d0707ce3d3e686b512d7 to your computer and use it in GitHub Desktop.
Server panel
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>
);
}

A server Panel that's not a panel

Note

code presented here, readme coming soon

feedback welcomed

import {
LayoutDashboard,
Globe,
FileText,
Users,
Code,
Github,
Terminal,
Server,
Zap,
Link,
Database,
Cpu
} from 'lucide-react';
import { Button } from './ui/button';
interface SidebarProps {
activeSection: string;
onSectionChange: (section: string) => void;
}
export function Sidebar({ activeSection, onSectionChange }: SidebarProps) {
const menuItems = [
{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ id: 'apps', label: 'Apps & Sites', icon: Globe },
{ id: 'domains', label: 'Domains', icon: Link },
{ id: 'server', label: 'Server Config', icon: Database },
{ id: 'nodejs', label: 'Node.js CLI', icon: Cpu },
{ id: 'content', label: 'Content', icon: FileText },
{ id: 'users', label: 'Users', icon: Users },
{ id: 'codespace', label: 'Codespace', icon: Code },
{ id: 'github', label: 'GitHub', icon: Github },
{ id: 'vscode', label: 'VS Code', icon: Terminal },
];
return (
<div className="fixed left-0 top-0 h-full w-64 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-700 shadow-lg">
<div className="p-6">
<div className="flex items-center gap-3 mb-8">
<div className="w-10 h-10 bg-gradient-to-r from-orange-500 to-red-500 rounded-lg flex items-center justify-center">
<Server className="w-6 h-6 text-white" />
</div>
<div>
<h1 className="text-lg font-bold text-slate-900 dark:text-white">ServerPanel</h1>
<p className="text-xs text-slate-500">Admin Control</p>
</div>
</div>
<nav className="space-y-2">
{menuItems.map((item) => {
const Icon = item.icon;
const isActive = activeSection === item.id;
return (
<Button
key={item.id}
variant={isActive ? "default" : "ghost"}
className={`w-full justify-start gap-3 h-12 ${
isActive
? 'bg-gradient-to-r from-orange-500 to-red-500 text-white shadow-lg'
: 'text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800'
}`}
onClick={() => onSectionChange(item.id)}
>
<Icon className="w-5 h-5" />
{item.label}
</Button>
);
})}
</nav>
<div className="absolute bottom-6 left-6 right-6">
<div className="bg-gradient-to-r from-orange-100 to-red-100 dark:from-orange-900/20 dark:to-red-900/20 rounded-lg p-4">
<div className="flex items-center gap-2 mb-2">
<Zap className="w-4 h-4 text-orange-600" />
<span className="text-sm font-medium text-slate-700 dark:text-slate-300">Server Status</span>
</div>
<div className="text-xs text-slate-600 dark:text-slate-400">
<div className="flex justify-between">
<span>CPU:</span>
<span className="text-green-600">24%</span>
</div>
<div className="flex justify-between">
<span>RAM:</span>
<span className="text-yellow-600">68%</span>
</div>
<div className="flex justify-between">
<span>Disk:</span>
<span className="text-blue-600">45%</span>
</div>
</div>
</div>
</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>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment