feat: initialize Infra Manager with React, Tailwind CSS, and Inertia.js
- Added main application entry point (app.jsx) and bootstrap file for React setup. - Created Wiki index page (Index.jsx) for documentation and file management. - Implemented utility functions for formatting and status handling in utils.js. - Set up Blade template (app.blade.php) for Inertia.js integration. - Defined API routes for metrics and terminal access in api.php. - Established console commands for scheduled tasks in console.php. - Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php. - Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments. - Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js. - Set up Vite for asset bundling and development server in vite.config.js.
This commit is contained in:
114
resources/js/Pages/Hypervisor/Index.jsx
Normal file
114
resources/js/Pages/Hypervisor/Index.jsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import AppLayout from '@/Layouts/AppLayout';
|
||||
import { Head, router } from '@inertiajs/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Plus, RefreshCw, Server, TestTube, Edit, Trash2, Eye } from 'lucide-react';
|
||||
import { GlassCard } from '@/Components/ui/GlassCard';
|
||||
import { StatusBadge } from '@/Components/ui/StatusBadge';
|
||||
import { Button } from '@/Components/ui/Button';
|
||||
import { DataTable } from '@/Components/ui/DataTable';
|
||||
import { timeAgo } from '@/lib/utils';
|
||||
import { useState } from 'react';
|
||||
|
||||
const TYPE_LABELS = {
|
||||
vcenter: 'VMware vCenter', esxi: 'VMware ESXi', proxmox: 'Proxmox VE',
|
||||
'hyper-v': 'Hyper-V', xen: 'XenServer', kvm: 'KVM',
|
||||
};
|
||||
const TYPE_COLORS = {
|
||||
vcenter: 'text-blue-400', esxi: 'text-blue-300', proxmox: 'text-orange-400',
|
||||
'hyper-v': 'text-blue-500', kvm: 'text-green-400',
|
||||
};
|
||||
|
||||
export default function HypervisorIndex({ hypervisors }) {
|
||||
const [syncing, setSyncing] = useState(null);
|
||||
|
||||
const sync = (id) => {
|
||||
setSyncing(id);
|
||||
router.post(`/hypervisors/${id}/sync`, {}, {
|
||||
onFinish: () => setSyncing(null),
|
||||
});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ header: 'Name', accessor: 'name', render: row => (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-white/5 border border-white/10 flex items-center justify-center">
|
||||
<Server className="w-4 h-4 text-white/50" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-white">{row.name}</p>
|
||||
<p className={`text-xs ${TYPE_COLORS[row.type] ?? 'text-white/40'}`}>{TYPE_LABELS[row.type] ?? row.type}</p>
|
||||
</div>
|
||||
</div>
|
||||
)},
|
||||
{ header: 'Host', accessor: 'host', render: row => (
|
||||
<span className="font-mono text-xs text-white/60">{row.host}:{row.port}</span>
|
||||
)},
|
||||
{ header: 'VMs', key: 'vms', render: row => (
|
||||
<span className="text-white font-medium">{row.virtual_machines_count ?? 0}</span>
|
||||
)},
|
||||
{ header: 'Status', accessor: 'status', render: row => <StatusBadge status={row.status} /> },
|
||||
{ header: 'Last Sync', accessor: 'last_sync_at', render: row => (
|
||||
<span className="text-xs text-white/40">{timeAgo(row.last_sync_at)}</span>
|
||||
)},
|
||||
{ header: '', key: 'actions', render: row => (
|
||||
<div className="flex items-center gap-1" onClick={e => e.stopPropagation()}>
|
||||
<button onClick={() => sync(row.id)} disabled={syncing === row.id}
|
||||
className="p-1.5 rounded-lg hover:bg-white/10 text-white/40 hover:text-cyan-400 transition-colors"
|
||||
title="Sync">
|
||||
<RefreshCw className={`w-3.5 h-3.5 ${syncing === row.id ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
<a href={`/hypervisors/${row.id}`} className="p-1.5 rounded-lg hover:bg-white/10 text-white/40 hover:text-white transition-colors" title="View">
|
||||
<Eye className="w-3.5 h-3.5" />
|
||||
</a>
|
||||
<a href={`/hypervisors/${row.id}/edit`} className="p-1.5 rounded-lg hover:bg-white/10 text-white/40 hover:text-white transition-colors" title="Edit">
|
||||
<Edit className="w-3.5 h-3.5" />
|
||||
</a>
|
||||
<button onClick={() => { if (confirm('Remove this hypervisor?')) router.delete(`/hypervisors/${row.id}`); }}
|
||||
className="p-1.5 rounded-lg hover:bg-red-500/20 text-white/40 hover:text-red-400 transition-colors" title="Delete">
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)},
|
||||
];
|
||||
|
||||
return (
|
||||
<AppLayout title="Hypervisors">
|
||||
<Head title="Hypervisors" />
|
||||
<div className="space-y-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-white">Hypervisors</h2>
|
||||
<p className="text-sm text-white/40">Manage vCenter, Proxmox, ESXi, Hyper-V and more</p>
|
||||
</div>
|
||||
<a href="/hypervisors/create">
|
||||
<Button icon={Plus}>Add Hypervisor</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ label: 'Total', value: hypervisors.total ?? 0, color: 'text-white' },
|
||||
{ label: 'Online', value: hypervisors.data?.filter(h => h.status === 'online').length ?? 0, color: 'text-green-400' },
|
||||
{ label: 'Offline', value: hypervisors.data?.filter(h => h.status === 'offline').length ?? 0, color: 'text-red-400' },
|
||||
{ label: 'Error', value: hypervisors.data?.filter(h => h.status === 'error').length ?? 0, color: 'text-orange-400' },
|
||||
].map(s => (
|
||||
<GlassCard key={s.label} className="p-4 text-center">
|
||||
<p className={`text-2xl font-bold ${s.color}`}>{s.value}</p>
|
||||
<p className="text-xs text-white/40 mt-0.5">{s.label}</p>
|
||||
</GlassCard>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<GlassCard className="p-5">
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={hypervisors.data ?? []}
|
||||
onRowClick={row => router.visit(`/hypervisors/${row.id}`)}
|
||||
emptyMessage="No hypervisors configured. Add your first one!"
|
||||
/>
|
||||
</GlassCard>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user