96 lines
No EOL
3.1 KiB
TypeScript
96 lines
No EOL
3.1 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { Toaster } from 'react-hot-toast';
|
|
import { Plus, Server as ServerIcon } from 'lucide-react';
|
|
import { AddServerModal } from './components/AddServerModal';
|
|
import { ServerCard } from './components/ServerCard';
|
|
import { storage } from './utils/storage';
|
|
import type { Server } from './types';
|
|
|
|
function App() {
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [servers, setServers] = useState<Server[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const fetchServers = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const serverData = await storage.getServers();
|
|
setServers(serverData);
|
|
} catch (error) {
|
|
console.error('Error fetching servers:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleDeleteServer = async (id: string) => {
|
|
try {
|
|
await storage.deleteServer(id);
|
|
await fetchServers();
|
|
} catch (error) {
|
|
console.error('Error deleting server:', error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchServers();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="min-h-screen gradient-bg">
|
|
<Toaster position="top-right" />
|
|
|
|
<div className="container mx-auto px-4 py-8">
|
|
<div className="flex flex-col md:flex-row justify-between items-center mb-12 gap-4">
|
|
<div className="flex items-center gap-3">
|
|
<ServerIcon size={32} className="text-purple-400" />
|
|
<h1 className="text-4xl font-bold text-white bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-pink-300">
|
|
Proxmox Dashboard
|
|
</h1>
|
|
</div>
|
|
|
|
<button
|
|
onClick={() => setIsModalOpen(true)}
|
|
className="bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-lg flex items-center gap-2 transform hover:scale-105 transition-all duration-200 shadow-lg hover:shadow-purple-500/20"
|
|
>
|
|
<Plus size={20} />
|
|
Add Server
|
|
</button>
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div className="text-center py-16">
|
|
<div className="animate-spin text-purple-400 mb-4">
|
|
<ServerIcon size={48} />
|
|
</div>
|
|
<p className="text-gray-300">Loading servers...</p>
|
|
</div>
|
|
) : servers.length === 0 ? (
|
|
<div className="text-center py-16">
|
|
<ServerIcon size={48} className="text-purple-400 mx-auto mb-4 opacity-50" />
|
|
<h2 className="text-2xl font-semibold text-gray-300 mb-2">No servers yet</h2>
|
|
<p className="text-gray-400">Add your first Proxmox server to get started</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{servers.map((server) => (
|
|
<ServerCard
|
|
key={server.id}
|
|
server={server}
|
|
onDelete={handleDeleteServer}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<AddServerModal
|
|
isOpen={isModalOpen}
|
|
onClose={() => setIsModalOpen(false)}
|
|
onServerAdded={fetchServers}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App; |