Update virtual-machines.tsx

This commit is contained in:
MacRimi
2026-02-03 17:00:43 +01:00
parent 7c2d6d6618
commit 4b7c9a1bd3

View File

@@ -1243,13 +1243,18 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
<div className="flex-1 overflow-hidden px-6 py-4"> <div className="flex-1 overflow-hidden px-6 py-4">
{/* Mobile carousel container */} {/* Mobile carousel container */}
<div className="sm:hidden flex flex-col h-full"> <div className="sm:hidden flex flex-col" style={{ height: 'calc(100vh - 320px)' }}>
<div className="flex-1 overflow-hidden relative"> <div className="flex-1 relative overflow-hidden">
{/* Page 0: Main content */}
<div <div
className={`absolute inset-0 overflow-y-auto transition-opacity duration-300 ${modalPage === 0 ? 'opacity-100 z-10' : 'opacity-0 z-0 pointer-events-none'}`} className="flex transition-transform duration-300 ease-in-out"
style={{
transform: `translateX(-${modalPage * 50}%)`,
width: '200%'
}}
> >
<div className="space-y-6"> {/* Page 0: Main content */}
<div className="w-1/2 h-full overflow-y-auto pr-3">
<div className="space-y-6">
{selectedVM && ( {selectedVM && (
<> <>
<div key={`metrics-mobile-${selectedVM.vmid}`}> <div key={`metrics-mobile-${selectedVM.vmid}`}>
@@ -1366,16 +1371,14 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
</CardContent> </CardContent>
</Card> </Card>
) : null} ) : null}
</> </>
)} )}
</div>
</div> </div>
</div>
{/* Page 1: Backups */} {/* Page 1: Backups */}
<div <div className="w-1/2 h-full overflow-y-auto pl-3">
className={`absolute inset-0 overflow-y-auto transition-opacity duration-300 ${modalPage === 1 ? 'opacity-100 z-10' : 'opacity-0 z-0 pointer-events-none'}`} <div className="space-y-4">
>
<div className="space-y-4">
<Card className="border border-border bg-card/50"> <Card className="border border-border bg-card/50">
<CardContent className="p-4"> <CardContent className="p-4">
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-4">Create Backup</h3> <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-4">Create Backup</h3>
@@ -1447,7 +1450,7 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
</div> </div>
{/* Mobile pagination dots */} {/* Mobile pagination dots */}
<div className="flex justify-center gap-3 py-3"> <div className="flex justify-center gap-3 pt-4 pb-2">
<button <button
onClick={() => setModalPage(0)} onClick={() => setModalPage(0)}
className={`w-3 h-3 rounded-full transition-colors ${modalPage === 0 ? 'bg-primary' : 'bg-zinc-500'}`} className={`w-3 h-3 rounded-full transition-colors ${modalPage === 0 ? 'bg-primary' : 'bg-zinc-500'}`}
@@ -1548,96 +1551,19 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
</div> </div>
</div> </div>
</CardContent> </CardContent>
</Card>
</div>
{/* Desktop Backups Section - Always visible */}
<Card className="border border-border bg-card/50">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
Backups
</h3>
<Button
variant="outline"
size="sm"
onClick={() => selectedVM && fetchVmBackups(selectedVM.vmid)}
className="text-xs bg-transparent"
>
Refresh
</Button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Create Backup */}
<div className="space-y-3">
<label className="text-xs text-muted-foreground block">Create New Backup</label>
<div className="flex gap-2">
<Select value={selectedBackupStorage} onValueChange={setSelectedBackupStorage}>
<SelectTrigger className="flex-1">
<SelectValue placeholder="Select storage" />
</SelectTrigger>
<SelectContent>
{backupStorages.map((storage) => (
<SelectItem key={`desktop-storage-${storage.storage}`} value={storage.storage}>
{storage.storage} ({storage.avail_human} free)
</SelectItem>
))}
</SelectContent>
</Select>
<Button
className="bg-amber-600 hover:bg-amber-700 text-white"
onClick={handleCreateBackup}
disabled={creatingBackup || !selectedBackupStorage}
>
{creatingBackup ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Archive className="h-4 w-4" />
)}
</Button>
</div>
</div>
{/* Backup List */}
<div>
<label className="text-xs text-muted-foreground block mb-2">
Existing Backups ({vmBackups.length})
</label>
{loadingBackups ? (
<div className="text-center py-2 text-muted-foreground text-sm">
<Loader2 className="h-4 w-4 animate-spin inline mr-2" />
Loading...
</div>
) : vmBackups.length === 0 ? (
<div className="text-center py-2 text-muted-foreground text-sm">
No backups found
</div>
) : (
<div className="space-y-1 max-h-32 overflow-y-auto">
{vmBackups.map((backup, index) => (
<div key={`desktop-backup-top-${backup.volid}-${index}`} className="flex justify-between items-center text-sm p-2 rounded bg-muted/30">
<span>{backup.date}</span>
<Badge variant="outline" className="text-xs">{backup.size_human}</Badge>
</div>
))}
</div>
)}
</div>
</div>
</CardContent>
</Card> </Card>
</div>
{detailsLoading ? ( {detailsLoading ? (
<div className="text-center py-8 text-muted-foreground">Loading configuration...</div> <div className="text-center py-8 text-muted-foreground">Loading configuration...</div>
) : vmDetails?.config ? ( ) : vmDetails?.config ? (
<> <>
<Card className="border border-border bg-card/50"> <Card className="border border-border bg-card/50" key={`config-${selectedVM.vmid}`}>
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide"> <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
Resources Resources
</h3> </h3>
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button
variant="outline" variant="outline"
@@ -2158,6 +2084,83 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
)} )}
</CardContent> </CardContent>
</Card> </Card>
{/* Desktop Backups Section */}
<Card className="border border-border bg-card/50">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
Backups
</h3>
<Button
variant="outline"
size="sm"
onClick={() => selectedVM && fetchVmBackups(selectedVM.vmid)}
className="text-xs bg-transparent"
>
Refresh
</Button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Create Backup */}
<div className="space-y-3">
<label className="text-xs text-muted-foreground block">Create New Backup</label>
<div className="flex gap-2">
<Select value={selectedBackupStorage} onValueChange={setSelectedBackupStorage}>
<SelectTrigger className="flex-1">
<SelectValue placeholder="Select storage" />
</SelectTrigger>
<SelectContent>
{backupStorages.map((storage) => (
<SelectItem key={storage.storage} value={storage.storage}>
{storage.storage} ({storage.avail_human} free)
</SelectItem>
))}
</SelectContent>
</Select>
<Button
className="bg-amber-600 hover:bg-amber-700 text-white"
onClick={handleCreateBackup}
disabled={creatingBackup || !selectedBackupStorage}
>
{creatingBackup ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Archive className="h-4 w-4" />
)}
</Button>
</div>
</div>
{/* Backup List */}
<div>
<label className="text-xs text-muted-foreground block mb-2">
Existing Backups ({vmBackups.length})
</label>
{loadingBackups ? (
<div className="text-center py-2 text-muted-foreground text-sm">
<Loader2 className="h-4 w-4 animate-spin inline mr-2" />
Loading...
</div>
) : vmBackups.length === 0 ? (
<div className="text-center py-2 text-muted-foreground text-sm">
No backups found
</div>
) : (
<div className="space-y-1 max-h-48 overflow-y-auto">
{vmBackups.map((backup, index) => (
<div key={`desktop-backup-${backup.volid}-${index}`} className="flex justify-between items-center text-sm p-2 rounded bg-muted/30">
<span>{backup.date}</span>
<Badge variant="outline" className="text-xs">{backup.size_human}</Badge>
</div>
))}
</div>
)}
</div>
</div>
</CardContent>
</Card>
</> </>
) : null} ) : null}
</> </>