Preview
Full width desktop view
Code
monitoring-10.tsx
1"use client";
2import React, { useState } from "react";
3import {
4 Card,
5 CardContent,
6 CardDescription,
7 CardHeader,
8 CardTitle,
9} from "@/components/ui/card";
10import { Button } from "@/components/ui/button";
11import { Input } from "@/components/ui/input";
12import { Label } from "@/components/ui/label";
13import {
14 Select,
15 SelectContent,
16 SelectItem,
17 SelectTrigger,
18 SelectValue,
19} from "@/components/ui/select";
20import { Badge } from "@/components/ui/badge";
21import {
22 Table,
23 TableBody,
24 TableCell,
25 TableHead,
26 TableHeader,
27 TableRow,
28} from "@/components/ui/table";
29import {
30 Dialog,
31 DialogContent,
32 DialogDescription,
33 DialogHeader,
34 DialogTitle,
35 DialogTrigger,
36} from "@/components/ui/dialog";
37import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
38import { Switch } from "@/components/ui/switch";
39import { Textarea } from "@/components/ui/textarea";
40import { Progress } from "@/components/ui/progress";
41import {
42 Server,
43 Plus,
44 Settings,
45 Activity,
46 HardDrive,
47 Cpu,
48 MemoryStick,
49 Network,
50 Power,
51 PowerOff,
52 Trash2,
53 Edit,
54 Eye,
55 AlertTriangle,
56 CheckCircle,
57 Clock,
58} from "lucide-react";
59
60interface VPSInstance {
61 id: string;
62 name: string;
63 type: "shared" | "dedicated";
64 status: "running" | "stopped" | "maintenance" | "error";
65 customer: string;
66 plan: string;
67 cpu: number;
68 ram: number;
69 storage: number;
70 bandwidth: number;
71 ipAddress: string;
72 location: string;
73 createdAt: string;
74 cpuUsage: number;
75 ramUsage: number;
76 storageUsage: number;
77 uptime: string;
78}
79
80interface NewVPSForm {
81 name: string;
82 type: "shared" | "dedicated";
83 customer: string;
84 plan: string;
85 cpu: number;
86 ram: number;
87 storage: number;
88 bandwidth: number;
89 location: string;
90 autoBackup: boolean;
91 notes: string;
92}
93
94const VPSAdminDashboard: React.FC = () => {
95 const [instances, setInstances] = useState<VPSInstance[]>([
96 {
97 id: "vps-001",
98 name: "WebStore-Primary",
99 type: "dedicated",
100 status: "running",
101 customer: "John Doe Store",
102 plan: "Premium",
103 cpu: 4,
104 ram: 8,
105 storage: 200,
106 bandwidth: 1000,
107 ipAddress: "192.168.1.100",
108 location: "US-East",
109 createdAt: "2024-01-15",
110 cpuUsage: 45,
111 ramUsage: 62,
112 storageUsage: 78,
113 uptime: "99.9%",
114 },
115 {
116 id: "vps-002",
117 name: "ECommerce-Dev",
118 type: "shared",
119 status: "running",
120 customer: "Tech Solutions Inc",
121 plan: "Standard",
122 cpu: 2,
123 ram: 4,
124 storage: 100,
125 bandwidth: 500,
126 ipAddress: "192.168.1.101",
127 location: "EU-West",
128 createdAt: "2024-01-20",
129 cpuUsage: 23,
130 ramUsage: 41,
131 storageUsage: 34,
132 uptime: "99.5%",
133 },
134 {
135 id: "vps-003",
136 name: "Store-Backup",
137 type: "shared",
138 status: "stopped",
139 customer: "Small Business Co",
140 plan: "Basic",
141 cpu: 1,
142 ram: 2,
143 storage: 50,
144 bandwidth: 250,
145 ipAddress: "192.168.1.102",
146 location: "US-West",
147 createdAt: "2024-02-01",
148 cpuUsage: 0,
149 ramUsage: 0,
150 storageUsage: 12,
151 uptime: "98.2%",
152 },
153 ]);
154
155 const [newVPS, setNewVPS] = useState<NewVPSForm>({
156 name: "",
157 type: "shared",
158 customer: "",
159 plan: "",
160 cpu: 1,
161 ram: 2,
162 storage: 50,
163 bandwidth: 250,
164 location: "",
165 autoBackup: false,
166 notes: "",
167 });
168
169 const [isDialogOpen, setIsDialogOpen] = useState(false);
170 const [selectedInstance, setSelectedInstance] = useState<VPSInstance | null>(
171 null
172 );
173
174 const getStatusColor = (status: string) => {
175 switch (status) {
176 case "running":
177 return "bg-green-500";
178 case "stopped":
179 return "bg-red-500";
180 case "maintenance":
181 return "bg-yellow-500";
182 case "error":
183 return "bg-orange-500";
184 default:
185 return "bg-gray-500";
186 }
187 };
188
189 const getStatusIcon = (status: string) => {
190 switch (status) {
191 case "running":
192 return <CheckCircle className="h-4 w-4" />;
193 case "stopped":
194 return <PowerOff className="h-4 w-4" />;
195 case "maintenance":
196 return <Clock className="h-4 w-4" />;
197 case "error":
198 return <AlertTriangle className="h-4 w-4" />;
199 default:
200 return <Power className="h-4 w-4" />;
201 }
202 };
203
204 const handleCreateVPS = () => {
205 const newInstance: VPSInstance = {
206 id: `vps-${String(instances.length + 1).padStart(3, "0")}`,
207 name: newVPS.name,
208 type: newVPS.type,
209 status: "running",
210 customer: newVPS.customer,
211 plan: newVPS.plan,
212 cpu: newVPS.cpu,
213 ram: newVPS.ram,
214 storage: newVPS.storage,
215 bandwidth: newVPS.bandwidth,
216 ipAddress: `192.168.1.${103 + instances.length}`,
217 location: newVPS.location,
218 createdAt: new Date().toISOString().split("T")[0],
219 cpuUsage: Math.floor(Math.random() * 50),
220 ramUsage: Math.floor(Math.random() * 60),
221 storageUsage: Math.floor(Math.random() * 40),
222 uptime: "100%",
223 };
224
225 setInstances([...instances, newInstance]);
226 setNewVPS({
227 name: "",
228 type: "shared",
229 customer: "",
230 plan: "",
231 cpu: 1,
232 ram: 2,
233 storage: 50,
234 bandwidth: 250,
235 location: "",
236 autoBackup: false,
237 notes: "",
238 });
239 setIsDialogOpen(false);
240 };
241
242 const handleStatusChange = (id: string, newStatus: "running" | "stopped") => {
243 setInstances(
244 instances.map((instance) =>
245 instance.id === id ? { ...instance, status: newStatus } : instance
246 )
247 );
248 };
249
250 const handleDeleteInstance = (id: string) => {
251 setInstances(instances.filter((instance) => instance.id !== id));
252 };
253
254 const totalInstances = instances.length;
255 const runningInstances = instances.filter(
256 (i) => i.status === "running"
257 ).length;
258 const totalCPU = instances.reduce((sum, i) => sum + i.cpu, 0);
259 const totalRAM = instances.reduce((sum, i) => sum + i.ram, 0);
260
261 return (
262 <div className="min-h-screen bg-background p-6">
263 <div className="max-w-7xl mx-auto space-y-6">
264 {/* Header */}
265 <div className="flex items-center justify-between">
266 <div>
267 <h1 className="text-3xl font-bold text-foreground">
268 VPS Management Dashboard
269 </h1>
270 <p className="text-muted-foreground">
271 Manage and monitor VPS instances for webstore deployments
272 </p>
273 </div>
274 <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
275 <DialogTrigger asChild>
276 <Button className="flex items-center gap-2">
277 <Plus className="h-4 w-4" />
278 Add New VPS
279 </Button>
280 </DialogTrigger>
281 <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
282 <DialogHeader>
283 <DialogTitle>Create New VPS Instance</DialogTitle>
284 <DialogDescription>
285 Configure a new VPS instance for webstore deployment
286 </DialogDescription>
287 </DialogHeader>
288 <div className="grid gap-4 py-4">
289 <div className="grid grid-cols-2 gap-4">
290 <div className="space-y-2">
291 <Label htmlFor="name">Instance Name</Label>
292 <Input
293 id="name"
294 value={newVPS.name}
295 onChange={(e) =>
296 setNewVPS({ ...newVPS, name: e.target.value })
297 }
298 placeholder="e.g., WebStore-Primary"
299 />
300 </div>
301 <div className="space-y-2">
302 <Label htmlFor="type">VPS Type</Label>
303 <Select
304 value={newVPS.type}
305 onValueChange={(value: "shared" | "dedicated") =>
306 setNewVPS({ ...newVPS, type: value })
307 }
308 >
309 <SelectTrigger>
310 <SelectValue />
311 </SelectTrigger>
312 <SelectContent>
313 <SelectItem value="shared">Shared VPS</SelectItem>
314 <SelectItem value="dedicated">Dedicated VPS</SelectItem>
315 </SelectContent>
316 </Select>
317 </div>
318 </div>
319
320 <div className="grid grid-cols-2 gap-4">
321 <div className="space-y-2">
322 <Label htmlFor="customer">Customer Name</Label>
323 <Input
324 id="customer"
325 value={newVPS.customer}
326 onChange={(e) =>
327 setNewVPS({ ...newVPS, customer: e.target.value })
328 }
329 placeholder="Customer or company name"
330 />
331 </div>
332 <div className="space-y-2">
333 <Label htmlFor="plan">Plan</Label>
334 <Select
335 value={newVPS.plan}
336 onValueChange={(value) =>
337 setNewVPS({ ...newVPS, plan: value })
338 }
339 >
340 <SelectTrigger>
341 <SelectValue placeholder="Select plan" />
342 </SelectTrigger>
343 <SelectContent>
344 <SelectItem value="basic">Basic</SelectItem>
345 <SelectItem value="standard">Standard</SelectItem>
346 <SelectItem value="premium">Premium</SelectItem>
347 <SelectItem value="enterprise">Enterprise</SelectItem>
348 </SelectContent>
349 </Select>
350 </div>
351 </div>
352
353 <div className="grid grid-cols-2 gap-4">
354 <div className="space-y-2">
355 <Label htmlFor="cpu">CPU Cores</Label>
356 <Select
357 value={String(newVPS.cpu)}
358 onValueChange={(value) =>
359 setNewVPS({ ...newVPS, cpu: parseInt(value) })
360 }
361 >
362 <SelectTrigger>
363 <SelectValue />
364 </SelectTrigger>
365 <SelectContent>
366 <SelectItem value="1">1 Core</SelectItem>
367 <SelectItem value="2">2 Cores</SelectItem>
368 <SelectItem value="4">4 Cores</SelectItem>
369 <SelectItem value="8">8 Cores</SelectItem>
370 <SelectItem value="16">16 Cores</SelectItem>
371 </SelectContent>
372 </Select>
373 </div>
374 <div className="space-y-2">
375 <Label htmlFor="ram">RAM (GB)</Label>
376 <Select
377 value={String(newVPS.ram)}
378 onValueChange={(value) =>
379 setNewVPS({ ...newVPS, ram: parseInt(value) })
380 }
381 >
382 <SelectTrigger>
383 <SelectValue />
384 </SelectTrigger>
385 <SelectContent>
386 <SelectItem value="2">2 GB</SelectItem>
387 <SelectItem value="4">4 GB</SelectItem>
388 <SelectItem value="8">8 GB</SelectItem>
389 <SelectItem value="16">16 GB</SelectItem>
390 <SelectItem value="32">32 GB</SelectItem>
391 <SelectItem value="64">64 GB</SelectItem>
392 </SelectContent>
393 </Select>
394 </div>
395 </div>
396
397 <div className="grid grid-cols-2 gap-4">
398 <div className="space-y-2">
399 <Label htmlFor="storage">Storage (GB)</Label>
400 <Select
401 value={String(newVPS.storage)}
402 onValueChange={(value) =>
403 setNewVPS({ ...newVPS, storage: parseInt(value) })
404 }
405 >
406 <SelectTrigger>
407 <SelectValue />
408 </SelectTrigger>
409 <SelectContent>
410 <SelectItem value="50">50 GB</SelectItem>
411 <SelectItem value="100">100 GB</SelectItem>
412 <SelectItem value="200">200 GB</SelectItem>
413 <SelectItem value="500">500 GB</SelectItem>
414 <SelectItem value="1000">1 TB</SelectItem>
415 </SelectContent>
416 </Select>
417 </div>
418 <div className="space-y-2">
419 <Label htmlFor="bandwidth">Bandwidth (GB/month)</Label>
420 <Select
421 value={String(newVPS.bandwidth)}
422 onValueChange={(value) =>
423 setNewVPS({ ...newVPS, bandwidth: parseInt(value) })
424 }
425 >
426 <SelectTrigger>
427 <SelectValue />
428 </SelectTrigger>
429 <SelectContent>
430 <SelectItem value="250">250 GB</SelectItem>
431 <SelectItem value="500">500 GB</SelectItem>
432 <SelectItem value="1000">1 TB</SelectItem>
433 <SelectItem value="2000">2 TB</SelectItem>
434 <SelectItem value="0">Unlimited</SelectItem>
435 </SelectContent>
436 </Select>
437 </div>
438 </div>
439
440 <div className="space-y-2">
441 <Label htmlFor="location">Location</Label>
442 <Select
443 value={newVPS.location}
444 onValueChange={(value) =>
445 setNewVPS({ ...newVPS, location: value })
446 }
447 >
448 <SelectTrigger>
449 <SelectValue placeholder="Select location" />
450 </SelectTrigger>
451 <SelectContent>
452 <SelectItem value="US-East">
453 US East (Virginia)
454 </SelectItem>
455 <SelectItem value="US-West">
456 US West (California)
457 </SelectItem>
458 <SelectItem value="EU-West">EU West (Ireland)</SelectItem>
459 <SelectItem value="EU-Central">
460 EU Central (Frankfurt)
461 </SelectItem>
462 <SelectItem value="Asia-Pacific">
463 Asia Pacific (Singapore)
464 </SelectItem>
465 </SelectContent>
466 </Select>
467 </div>
468
469 <div className="flex items-center space-x-2">
470 <Switch
471 id="autoBackup"
472 checked={newVPS.autoBackup}
473 onCheckedChange={(checked) =>
474 setNewVPS({ ...newVPS, autoBackup: checked })
475 }
476 />
477 <Label htmlFor="autoBackup">Enable automatic backups</Label>
478 </div>
479
480 <div className="space-y-2">
481 <Label htmlFor="notes">Notes (Optional)</Label>
482 <Textarea
483 id="notes"
484 value={newVPS.notes}
485 onChange={(e) =>
486 setNewVPS({ ...newVPS, notes: e.target.value })
487 }
488 placeholder="Additional notes or requirements..."
489 rows={3}
490 />
491 </div>
492
493 <div className="flex justify-end space-x-2 pt-4">
494 <Button
495 variant="outline"
496 onClick={() => setIsDialogOpen(false)}
497 >
498 Cancel
499 </Button>
500 <Button
501 onClick={handleCreateVPS}
502 disabled={
503 !newVPS.name ||
504 !newVPS.customer ||
505 !newVPS.plan ||
506 !newVPS.location
507 }
508 >
509 Create VPS Instance
510 </Button>
511 </div>
512 </div>
513 </DialogContent>
514 </Dialog>
515 </div>
516
517 {/* Stats Cards */}
518 <div className="grid grid-cols-1 md:grid-cols-4 gap-6">
519 <Card>
520 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
521 <CardTitle className="text-sm font-medium">
522 Total Instances
523 </CardTitle>
524 <Server className="h-4 w-4 text-muted-foreground" />
525 </CardHeader>
526 <CardContent>
527 <div className="text-2xl font-bold">{totalInstances}</div>
528 <p className="text-xs text-muted-foreground">
529 {runningInstances} running
530 </p>
531 </CardContent>
532 </Card>
533
534 <Card>
535 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
536 <CardTitle className="text-sm font-medium">
537 Total CPU Cores
538 </CardTitle>
539 <Cpu className="h-4 w-4 text-muted-foreground" />
540 </CardHeader>
541 <CardContent>
542 <div className="text-2xl font-bold">{totalCPU}</div>
543 <p className="text-xs text-muted-foreground">
544 Across all instances
545 </p>
546 </CardContent>
547 </Card>
548
549 <Card>
550 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
551 <CardTitle className="text-sm font-medium">Total RAM</CardTitle>
552 <MemoryStick className="h-4 w-4 text-muted-foreground" />
553 </CardHeader>
554 <CardContent>
555 <div className="text-2xl font-bold">{totalRAM} GB</div>
556 <p className="text-xs text-muted-foreground">Memory allocated</p>
557 </CardContent>
558 </Card>
559
560 <Card>
561 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
562 <CardTitle className="text-sm font-medium">
563 Active Customers
564 </CardTitle>
565 <Activity className="h-4 w-4 text-muted-foreground" />
566 </CardHeader>
567 <CardContent>
568 <div className="text-2xl font-bold">
569 {new Set(instances.map((i) => i.customer)).size}
570 </div>
571 <p className="text-xs text-muted-foreground">Unique customers</p>
572 </CardContent>
573 </Card>
574 </div>
575
576 {/* Main Content */}
577 <Tabs defaultValue="instances" className="space-y-4">
578 <TabsList>
579 <TabsTrigger value="instances">VPS Instances</TabsTrigger>
580 <TabsTrigger value="monitoring">Monitoring</TabsTrigger>
581 </TabsList>
582
583 <TabsContent value="instances" className="space-y-4">
584 <Card>
585 <CardHeader>
586 <CardTitle>VPS Instances</CardTitle>
587 <CardDescription>
588 Manage all VPS instances and their configurations
589 </CardDescription>
590 </CardHeader>
591 <CardContent>
592 <Table>
593 <TableHeader>
594 <TableRow>
595 <TableHead>Instance</TableHead>
596 <TableHead>Type</TableHead>
597 <TableHead>Status</TableHead>
598 <TableHead>Customer</TableHead>
599 <TableHead>Resources</TableHead>
600 <TableHead>Location</TableHead>
601 <TableHead>IP Address</TableHead>
602 <TableHead>Actions</TableHead>
603 </TableRow>
604 </TableHeader>
605 <TableBody>
606 {instances.map((instance) => (
607 <TableRow key={instance.id}>
608 <TableCell>
609 <div>
610 <div className="font-medium">{instance.name}</div>
611 <div className="text-sm text-muted-foreground">
612 {instance.id}
613 </div>
614 </div>
615 </TableCell>
616 <TableCell>
617 <Badge
618 variant={
619 instance.type === "dedicated"
620 ? "default"
621 : "secondary"
622 }
623 >
624 {instance.type}
625 </Badge>
626 </TableCell>
627 <TableCell>
628 <div className="flex items-center gap-2">
629 <div
630 className={`w-2 h-2 rounded-full ${getStatusColor(
631 instance.status
632 )}`}
633 />
634 <span className="capitalize">
635 {instance.status}
636 </span>
637 </div>
638 </TableCell>
639 <TableCell>
640 <div>
641 <div className="font-medium">
642 {instance.customer}
643 </div>
644 <div className="text-sm text-muted-foreground">
645 {instance.plan}
646 </div>
647 </div>
648 </TableCell>
649 <TableCell>
650 <div className="text-sm">
651 <div>
652 {instance.cpu} CPU • {instance.ram}GB RAM
653 </div>
654 <div className="text-muted-foreground">
655 {instance.storage}GB Storage
656 </div>
657 </div>
658 </TableCell>
659 <TableCell>{instance.location}</TableCell>
660 <TableCell className="font-mono text-sm">
661 {instance.ipAddress}
662 </TableCell>
663 <TableCell>
664 <div className="flex items-center gap-2">
665 <Button
666 size="sm"
667 variant="outline"
668 onClick={() => setSelectedInstance(instance)}
669 >
670 <Eye className="h-4 w-4" />
671 </Button>
672 <Button
673 size="sm"
674 variant="outline"
675 onClick={() =>
676 handleStatusChange(
677 instance.id,
678 instance.status === "running"
679 ? "stopped"
680 : "running"
681 )
682 }
683 >
684 {instance.status === "running" ? (
685 <PowerOff className="h-4 w-4" />
686 ) : (
687 <Power className="h-4 w-4" />
688 )}
689 </Button>
690 <Button
691 size="sm"
692 variant="outline"
693 onClick={() => handleDeleteInstance(instance.id)}
694 >
695 <Trash2 className="h-4 w-4" />
696 </Button>
697 </div>
698 </TableCell>
699 </TableRow>
700 ))}
701 </TableBody>
702 </Table>
703 </CardContent>
704 </Card>
705 </TabsContent>
706
707 <TabsContent value="monitoring" className="space-y-4">
708 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
709 {instances
710 .filter((i) => i.status === "running")
711 .map((instance) => (
712 <Card key={instance.id}>
713 <CardHeader>
714 <CardTitle className="text-lg">{instance.name}</CardTitle>
715 <CardDescription>{instance.customer}</CardDescription>
716 </CardHeader>
717 <CardContent className="space-y-4">
718 <div className="space-y-2">
719 <div className="flex justify-between text-sm">
720 <span>CPU Usage</span>
721 <span>{instance.cpuUsage}%</span>
722 </div>
723 <Progress value={instance.cpuUsage} className="h-2" />
724 </div>
725
726 <div className="space-y-2">
727 <div className="flex justify-between text-sm">
728 <span>RAM Usage</span>
729 <span>{instance.ramUsage}%</span>
730 </div>
731 <Progress value={instance.ramUsage} className="h-2" />
732 </div>
733
734 <div className="space-y-2">
735 <div className="flex justify-between text-sm">
736 <span>Storage Usage</span>
737 <span>{instance.storageUsage}%</span>
738 </div>
739 <Progress
740 value={instance.storageUsage}
741 className="h-2"
742 />
743 </div>
744
745 <div className="flex justify-between items-center pt-2">
746 <span className="text-sm text-muted-foreground">
747 Uptime
748 </span>
749 <Badge variant="outline">{instance.uptime}</Badge>
750 </div>
751 </CardContent>
752 </Card>
753 ))}
754 </div>
755 </TabsContent>
756 </Tabs>
757
758 {/* Instance Details Dialog */}
759 {selectedInstance && (
760 <Dialog
761 open={!!selectedInstance}
762 onOpenChange={() => setSelectedInstance(null)}
763 >
764 <DialogContent className="max-w-2xl">
765 <DialogHeader>
766 <DialogTitle>{selectedInstance.name} Details</DialogTitle>
767 <DialogDescription>
768 Detailed information about this VPS instance
769 </DialogDescription>
770 </DialogHeader>
771 <div className="grid gap-4 py-4">
772 <div className="grid grid-cols-2 gap-4">
773 <div>
774 <Label className="text-sm font-medium">Instance ID</Label>
775 <p className="text-sm text-muted-foreground">
776 {selectedInstance.id}
777 </p>
778 </div>
779 <div>
780 <Label className="text-sm font-medium">Type</Label>
781 <p className="text-sm text-muted-foreground capitalize">
782 {selectedInstance.type}
783 </p>
784 </div>
785 </div>
786
787 <div className="grid grid-cols-2 gap-4">
788 <div>
789 <Label className="text-sm font-medium">Customer</Label>
790 <p className="text-sm text-muted-foreground">
791 {selectedInstance.customer}
792 </p>
793 </div>
794 <div>
795 <Label className="text-sm font-medium">Plan</Label>
796 <p className="text-sm text-muted-foreground">
797 {selectedInstance.plan}
798 </p>
799 </div>
800 </div>
801
802 <div className="grid grid-cols-2 gap-4">
803 <div>
804 <Label className="text-sm font-medium">IP Address</Label>
805 <p className="text-sm text-muted-foreground font-mono">
806 {selectedInstance.ipAddress}
807 </p>
808 </div>
809 <div>
810 <Label className="text-sm font-medium">Location</Label>
811 <p className="text-sm text-muted-foreground">
812 {selectedInstance.location}
813 </p>
814 </div>
815 </div>
816
817 <div className="grid grid-cols-4 gap-4">
818 <div>
819 <Label className="text-sm font-medium">CPU</Label>
820 <p className="text-sm text-muted-foreground">
821 {selectedInstance.cpu} cores
822 </p>
823 </div>
824 <div>
825 <Label className="text-sm font-medium">RAM</Label>
826 <p className="text-sm text-muted-foreground">
827 {selectedInstance.ram} GB
828 </p>
829 </div>
830 <div>
831 <Label className="text-sm font-medium">Storage</Label>
832 <p className="text-sm text-muted-foreground">
833 {selectedInstance.storage} GB
834 </p>
835 </div>
836 <div>
837 <Label className="text-sm font-medium">Bandwidth</Label>
838 <p className="text-sm text-muted-foreground">
839 {selectedInstance.bandwidth} GB
840 </p>
841 </div>
842 </div>
843
844 <div>
845 <Label className="text-sm font-medium">Created</Label>
846 <p className="text-sm text-muted-foreground">
847 {selectedInstance.createdAt}
848 </p>
849 </div>
850 </div>
851 </DialogContent>
852 </Dialog>
853 )}
854 </div>
855 </div>
856 );
857};
858
859export default VPSAdminDashboard;
Dependencies
External Libraries
lucide-reactreact
Shadcn/UI Components
badgebuttoncarddialoginputlabelprogressselectswitchtabletabstextarea