ShadcnUI Vaults

Back to Blocks

VPS Management Dashboard

Unknown Block

UnknownComponent

VPS Management Dashboard

Comprehensive VPS management dashboard for creating, monitoring, and managing VPS parents, shared, and dedicated instances with real-time status and activity logs.

Preview

Full width desktop view

Code

monitoring-6.tsx
1"use client";
2
3import React, { useState, useEffect } from "react";
4import {
5  Card,
6  CardContent,
7  CardDescription,
8  CardHeader,
9  CardTitle,
10} from "@/components/ui/card";
11import { Button } from "@/components/ui/button";
12import { Input } from "@/components/ui/input";
13import { Label } from "@/components/ui/label";
14import {
15  Select,
16  SelectContent,
17  SelectItem,
18  SelectTrigger,
19  SelectValue,
20} from "@/components/ui/select";
21import { Badge } from "@/components/ui/badge";
22import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
23import {
24  Dialog,
25  DialogContent,
26  DialogDescription,
27  DialogHeader,
28  DialogTitle,
29  DialogTrigger,
30} from "@/components/ui/dialog";
31import {
32  Table,
33  TableBody,
34  TableCell,
35  TableHead,
36  TableHeader,
37  TableRow,
38} from "@/components/ui/table";
39import { Progress } from "@/components/ui/progress";
40import { Switch } from "@/components/ui/switch";
41import { Separator } from "@/components/ui/separator";
42import {
43  Server,
44  Plus,
45  Settings,
46  Activity,
47  Cpu,
48  HardDrive,
49  MemoryStick,
50  Globe,
51  Users,
52  Power,
53  PowerOff,
54  Trash2,
55  Edit,
56  Eye,
57  AlertTriangle,
58  CheckCircle,
59  Clock,
60  Database,
61} from "lucide-react";
62
63interface VPSParent {
64  id: string;
65  name: string;
66  region: string;
67  cpu: number;
68  ram: number;
69  storage: number;
70  status: "online" | "offline" | "maintenance";
71  usedCpu: number;
72  usedRam: number;
73  usedStorage: number;
74  sharedVpsCount: number;
75  dedicatedVpsCount: number;
76  createdAt: string;
77}
78
79interface SharedVPS {
80  id: string;
81  name: string;
82  parentId: string;
83  parentName: string;
84  cpu: number;
85  ram: number;
86  storage: number;
87  status: "running" | "stopped" | "suspended";
88  customerEmail: string;
89  domain?: string;
90  createdAt: string;
91}
92
93interface DedicatedVPS {
94  id: string;
95  name: string;
96  region: string;
97  cpu: number;
98  ram: number;
99  storage: number;
100  status: "running" | "stopped" | "suspended";
101  customerEmail: string;
102  domain?: string;
103  createdAt: string;
104}
105
106const VPSAdminDashboard = () => {
107  const [vpsParents, setVpsParents] = useState<VPSParent[]>([
108    {
109      id: "1",
110      name: "US-East-Parent-01",
111      region: "us-east-1",
112      cpu: 32,
113      ram: 128,
114      storage: 2000,
115      status: "online",
116      usedCpu: 18,
117      usedRam: 76,
118      usedStorage: 1200,
119      sharedVpsCount: 8,
120      dedicatedVpsCount: 2,
121      createdAt: "2024-01-15",
122    },
123    {
124      id: "2",
125      name: "EU-West-Parent-01",
126      region: "eu-west-1",
127      cpu: 24,
128      ram: 96,
129      storage: 1500,
130      status: "online",
131      usedCpu: 12,
132      usedRam: 48,
133      usedStorage: 800,
134      sharedVpsCount: 6,
135      dedicatedVpsCount: 1,
136      createdAt: "2024-01-20",
137    },
138  ]);
139
140  const [sharedVps, setSharedVps] = useState<SharedVPS[]>([
141    {
142      id: "s1",
143      name: "Shared-Store-01",
144      parentId: "1",
145      parentName: "US-East-Parent-01",
146      cpu: 2,
147      ram: 8,
148      storage: 100,
149      status: "running",
150      customerEmail: "customer1@example.com",
151      domain: "store1.example.com",
152      createdAt: "2024-02-01",
153    },
154    {
155      id: "s2",
156      name: "Shared-Store-02",
157      parentId: "1",
158      parentName: "US-East-Parent-01",
159      cpu: 1,
160      ram: 4,
161      storage: 50,
162      status: "running",
163      customerEmail: "customer2@example.com",
164      domain: "store2.example.com",
165      createdAt: "2024-02-03",
166    },
167  ]);
168
169  const [dedicatedVps, setDedicatedVps] = useState<DedicatedVPS[]>([
170    {
171      id: "d1",
172      name: "Dedicated-Enterprise-01",
173      region: "us-east-1",
174      cpu: 8,
175      ram: 32,
176      storage: 500,
177      status: "running",
178      customerEmail: "enterprise@example.com",
179      domain: "enterprise.example.com",
180      createdAt: "2024-01-25",
181    },
182  ]);
183
184  const [activeTab, setActiveTab] = useState("overview");
185  const [isCreateParentOpen, setIsCreateParentOpen] = useState(false);
186  const [isCreateSharedOpen, setIsCreateSharedOpen] = useState(false);
187  const [isCreateDedicatedOpen, setIsCreateDedicatedOpen] = useState(false);
188
189  const [newParent, setNewParent] = useState({
190    name: "",
191    region: "",
192    cpu: 0,
193    ram: 0,
194    storage: 0,
195  });
196
197  const [newShared, setNewShared] = useState({
198    name: "",
199    parentId: "",
200    cpu: 0,
201    ram: 0,
202    storage: 0,
203    customerEmail: "",
204    domain: "",
205  });
206
207  const [newDedicated, setNewDedicated] = useState({
208    name: "",
209    region: "",
210    cpu: 0,
211    ram: 0,
212    storage: 0,
213    customerEmail: "",
214    domain: "",
215  });
216
217  const getStatusColor = (status: string) => {
218    switch (status) {
219      case "online":
220      case "running":
221        return "bg-green-500";
222      case "offline":
223      case "stopped":
224        return "bg-red-500";
225      case "maintenance":
226      case "suspended":
227        return "bg-yellow-500";
228      default:
229        return "bg-gray-500";
230    }
231  };
232
233  const getStatusIcon = (status: string) => {
234    switch (status) {
235      case "online":
236      case "running":
237        return <CheckCircle className="h-4 w-4" />;
238      case "offline":
239      case "stopped":
240        return <PowerOff className="h-4 w-4" />;
241      case "maintenance":
242      case "suspended":
243        return <AlertTriangle className="h-4 w-4" />;
244      default:
245        return <Clock className="h-4 w-4" />;
246    }
247  };
248
249  const createVPSParent = () => {
250    const parent: VPSParent = {
251      id: Date.now().toString(),
252      ...newParent,
253      status: "online",
254      usedCpu: 0,
255      usedRam: 0,
256      usedStorage: 0,
257      sharedVpsCount: 0,
258      dedicatedVpsCount: 0,
259      createdAt: new Date().toISOString().split("T")[0],
260    };
261    setVpsParents([...vpsParents, parent]);
262    setNewParent({ name: "", region: "", cpu: 0, ram: 0, storage: 0 });
263    setIsCreateParentOpen(false);
264  };
265
266  const createSharedVPS = () => {
267    const shared: SharedVPS = {
268      id: Date.now().toString(),
269      ...newShared,
270      parentName:
271        vpsParents.find((p) => p.id === newShared.parentId)?.name || "",
272      status: "running",
273      createdAt: new Date().toISOString().split("T")[0],
274    };
275    setSharedVps([...sharedVps, shared]);
276    setNewShared({
277      name: "",
278      parentId: "",
279      cpu: 0,
280      ram: 0,
281      storage: 0,
282      customerEmail: "",
283      domain: "",
284    });
285    setIsCreateSharedOpen(false);
286  };
287
288  const createDedicatedVPS = () => {
289    const dedicated: DedicatedVPS = {
290      id: Date.now().toString(),
291      ...newDedicated,
292      status: "running",
293      createdAt: new Date().toISOString().split("T")[0],
294    };
295    setDedicatedVps([...dedicatedVps, dedicated]);
296    setNewDedicated({
297      name: "",
298      region: "",
299      cpu: 0,
300      ram: 0,
301      storage: 0,
302      customerEmail: "",
303      domain: "",
304    });
305    setIsCreateDedicatedOpen(false);
306  };
307
308  const totalVpsParents = vpsParents.length;
309  const totalSharedVps = sharedVps.length;
310  const totalDedicatedVps = dedicatedVps.length;
311  const totalActiveInstances =
312    vpsParents.filter((p) => p.status === "online").length +
313    sharedVps.filter((s) => s.status === "running").length +
314    dedicatedVps.filter((d) => d.status === "running").length;
315
316  return (
317    <div className="min-h-screen bg-background p-6">
318      <div className="max-w-7xl mx-auto space-y-6">
319        {/* Header */}
320        <div className="flex items-center justify-between">
321          <div>
322            <h1 className="text-3xl font-bold text-foreground">
323              VPS Management Dashboard
324            </h1>
325            <p className="text-muted-foreground">
326              Manage and monitor your VPS infrastructure
327            </p>
328          </div>
329          <div className="flex items-center space-x-2">
330            <Badge variant="outline" className="text-green-600">
331              <Activity className="h-3 w-3 mr-1" />
332              {totalActiveInstances} Active
333            </Badge>
334          </div>
335        </div>
336
337        {/* Overview Cards */}
338        <div className="grid grid-cols-1 md:grid-cols-4 gap-6">
339          <Card>
340            <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
341              <CardTitle className="text-sm font-medium">VPS Parents</CardTitle>
342              <Server className="h-4 w-4 text-muted-foreground" />
343            </CardHeader>
344            <CardContent>
345              <div className="text-2xl font-bold">{totalVpsParents}</div>
346              <p className="text-xs text-muted-foreground">Physical servers</p>
347            </CardContent>
348          </Card>
349
350          <Card>
351            <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
352              <CardTitle className="text-sm font-medium">Shared VPS</CardTitle>
353              <Users className="h-4 w-4 text-muted-foreground" />
354            </CardHeader>
355            <CardContent>
356              <div className="text-2xl font-bold">{totalSharedVps}</div>
357              <p className="text-xs text-muted-foreground">Shared instances</p>
358            </CardContent>
359          </Card>
360
361          <Card>
362            <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
363              <CardTitle className="text-sm font-medium">
364                Dedicated VPS
365              </CardTitle>
366              <Database className="h-4 w-4 text-muted-foreground" />
367            </CardHeader>
368            <CardContent>
369              <div className="text-2xl font-bold">{totalDedicatedVps}</div>
370              <p className="text-xs text-muted-foreground">
371                Dedicated instances
372              </p>
373            </CardContent>
374          </Card>
375
376          <Card>
377            <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
378              <CardTitle className="text-sm font-medium">
379                Total Active
380              </CardTitle>
381              <Power className="h-4 w-4 text-muted-foreground" />
382            </CardHeader>
383            <CardContent>
384              <div className="text-2xl font-bold">{totalActiveInstances}</div>
385              <p className="text-xs text-muted-foreground">Running instances</p>
386            </CardContent>
387          </Card>
388        </div>
389
390        {/* Main Content */}
391        <Tabs
392          value={activeTab}
393          onValueChange={setActiveTab}
394          className="space-y-6"
395        >
396          <TabsList className="grid w-full grid-cols-4">
397            <TabsTrigger value="overview">Overview</TabsTrigger>
398            <TabsTrigger value="parents">VPS Parents</TabsTrigger>
399            <TabsTrigger value="shared">Shared VPS</TabsTrigger>
400            <TabsTrigger value="dedicated">Dedicated VPS</TabsTrigger>
401          </TabsList>
402
403          <TabsContent value="overview" className="space-y-6">
404            <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
405              {/* VPS Parents Overview */}
406              <Card>
407                <CardHeader>
408                  <CardTitle className="flex items-center">
409                    <Server className="h-5 w-5 mr-2" />
410                    VPS Parents Status
411                  </CardTitle>
412                </CardHeader>
413                <CardContent className="space-y-4">
414                  {vpsParents.map((parent) => (
415                    <div
416                      key={parent.id}
417                      className="flex items-center justify-between p-3 border rounded-lg"
418                    >
419                      <div className="flex items-center space-x-3">
420                        <div
421                          className={`w-3 h-3 rounded-full ${getStatusColor(
422                            parent.status
423                          )}`}
424                        />
425                        <div>
426                          <p className="font-medium">{parent.name}</p>
427                          <p className="text-sm text-muted-foreground">
428                            {parent.region}
429                          </p>
430                        </div>
431                      </div>
432                      <div className="text-right">
433                        <p className="text-sm font-medium">
434                          {parent.sharedVpsCount + parent.dedicatedVpsCount}{" "}
435                          instances
436                        </p>
437                        <p className="text-xs text-muted-foreground">
438                          CPU: {Math.round((parent.usedCpu / parent.cpu) * 100)}
439                          %
440                        </p>
441                      </div>
442                    </div>
443                  ))}
444                </CardContent>
445              </Card>
446
447              {/* Recent Activity */}
448              <Card>
449                <CardHeader>
450                  <CardTitle className="flex items-center">
451                    <Activity className="h-5 w-5 mr-2" />
452                    Recent Activity
453                  </CardTitle>
454                </CardHeader>
455                <CardContent className="space-y-4">
456                  <div className="space-y-3">
457                    <div className="flex items-center space-x-3 p-3 border rounded-lg">
458                      <CheckCircle className="h-4 w-4 text-green-500" />
459                      <div>
460                        <p className="text-sm font-medium">
461                          New shared VPS created
462                        </p>
463                        <p className="text-xs text-muted-foreground">
464                          2 minutes ago
465                        </p>
466                      </div>
467                    </div>
468                    <div className="flex items-center space-x-3 p-3 border rounded-lg">
469                      <Power className="h-4 w-4 text-blue-500" />
470                      <div>
471                        <p className="text-sm font-medium">
472                          VPS Parent restarted
473                        </p>
474                        <p className="text-xs text-muted-foreground">
475                          15 minutes ago
476                        </p>
477                      </div>
478                    </div>
479                    <div className="flex items-center space-x-3 p-3 border rounded-lg">
480                      <Users className="h-4 w-4 text-purple-500" />
481                      <div>
482                        <p className="text-sm font-medium">
483                          Customer deployed webstore
484                        </p>
485                        <p className="text-xs text-muted-foreground">
486                          1 hour ago
487                        </p>
488                      </div>
489                    </div>
490                  </div>
491                </CardContent>
492              </Card>
493            </div>
494          </TabsContent>
495
496          <TabsContent value="parents" className="space-y-6">
497            <div className="flex items-center justify-between">
498              <h2 className="text-2xl font-bold">VPS Parents</h2>
499              <Dialog
500                open={isCreateParentOpen}
501                onOpenChange={setIsCreateParentOpen}
502              >
503                <DialogTrigger asChild>
504                  <Button>
505                    <Plus className="h-4 w-4 mr-2" />
506                    Add VPS Parent
507                  </Button>
508                </DialogTrigger>
509                <DialogContent>
510                  <DialogHeader>
511                    <DialogTitle>Create New VPS Parent</DialogTitle>
512                    <DialogDescription>
513                      Add a new physical server to host VPS instances
514                    </DialogDescription>
515                  </DialogHeader>
516                  <div className="space-y-4">
517                    <div>
518                      <Label htmlFor="parent-name">Server Name</Label>
519                      <Input
520                        id="parent-name"
521                        value={newParent.name}
522                        onChange={(e) =>
523                          setNewParent({ ...newParent, name: e.target.value })
524                        }
525                        placeholder="US-East-Parent-02"
526                      />
527                    </div>
528                    <div>
529                      <Label htmlFor="parent-region">Region</Label>
530                      <Select
531                        value={newParent.region}
532                        onValueChange={(value) =>
533                          setNewParent({ ...newParent, region: value })
534                        }
535                      >
536                        <SelectTrigger>
537                          <SelectValue placeholder="Select region" />
538                        </SelectTrigger>
539                        <SelectContent>
540                          <SelectItem value="us-east-1">US East 1</SelectItem>
541                          <SelectItem value="us-west-1">US West 1</SelectItem>
542                          <SelectItem value="eu-west-1">EU West 1</SelectItem>
543                          <SelectItem value="ap-southeast-1">
544                            Asia Pacific Southeast 1
545                          </SelectItem>
546                        </SelectContent>
547                      </Select>
548                    </div>
549                    <div className="grid grid-cols-3 gap-4">
550                      <div>
551                        <Label htmlFor="parent-cpu">CPU Cores</Label>
552                        <Input
553                          id="parent-cpu"
554                          type="number"
555                          value={newParent.cpu}
556                          onChange={(e) =>
557                            setNewParent({
558                              ...newParent,
559                              cpu: parseInt(e.target.value) || 0,
560                            })
561                          }
562                          placeholder="32"
563                        />
564                      </div>
565                      <div>
566                        <Label htmlFor="parent-ram">RAM (GB)</Label>
567                        <Input
568                          id="parent-ram"
569                          type="number"
570                          value={newParent.ram}
571                          onChange={(e) =>
572                            setNewParent({
573                              ...newParent,
574                              ram: parseInt(e.target.value) || 0,
575                            })
576                          }
577                          placeholder="128"
578                        />
579                      </div>
580                      <div>
581                        <Label htmlFor="parent-storage">Storage (GB)</Label>
582                        <Input
583                          id="parent-storage"
584                          type="number"
585                          value={newParent.storage}
586                          onChange={(e) =>
587                            setNewParent({
588                              ...newParent,
589                              storage: parseInt(e.target.value) || 0,
590                            })
591                          }
592                          placeholder="2000"
593                        />
594                      </div>
595                    </div>
596                    <Button onClick={createVPSParent} className="w-full">
597                      Create VPS Parent
598                    </Button>
599                  </div>
600                </DialogContent>
601              </Dialog>
602            </div>
603
604            <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
605              {vpsParents.map((parent) => (
606                <Card key={parent.id}>
607                  <CardHeader>
608                    <div className="flex items-center justify-between">
609                      <CardTitle className="flex items-center">
610                        {getStatusIcon(parent.status)}
611                        <span className="ml-2">{parent.name}</span>
612                      </CardTitle>
613                      <Badge
614                        variant={
615                          parent.status === "online" ? "default" : "secondary"
616                        }
617                      >
618                        {parent.status}
619                      </Badge>
620                    </div>
621                    <CardDescription>
622                      <Globe className="h-4 w-4 inline mr-1" />
623                      {parent.region}
624                    </CardDescription>
625                  </CardHeader>
626                  <CardContent className="space-y-4">
627                    <div className="grid grid-cols-3 gap-4 text-center">
628                      <div className="space-y-1">
629                        <Cpu className="h-4 w-4 mx-auto text-muted-foreground" />
630                        <p className="text-sm font-medium">
631                          {parent.cpu} Cores
632                        </p>
633                        <p className="text-xs text-muted-foreground">
634                          {parent.usedCpu} used
635                        </p>
636                      </div>
637                      <div className="space-y-1">
638                        <MemoryStick className="h-4 w-4 mx-auto text-muted-foreground" />
639                        <p className="text-sm font-medium">{parent.ram} GB</p>
640                        <p className="text-xs text-muted-foreground">
641                          {parent.usedRam} GB used
642                        </p>
643                      </div>
644                      <div className="space-y-1">
645                        <HardDrive className="h-4 w-4 mx-auto text-muted-foreground" />
646                        <p className="text-sm font-medium">
647                          {parent.storage} GB
648                        </p>
649                        <p className="text-xs text-muted-foreground">
650                          {parent.usedStorage} GB used
651                        </p>
652                      </div>
653                    </div>
654
655                    <Separator />
656
657                    <div className="space-y-3">
658                      <div>
659                        <div className="flex justify-between text-sm mb-1">
660                          <span>CPU Usage</span>
661                          <span>
662                            {Math.round((parent.usedCpu / parent.cpu) * 100)}%
663                          </span>
664                        </div>
665                        <Progress value={(parent.usedCpu / parent.cpu) * 100} />
666                      </div>
667                      <div>
668                        <div className="flex justify-between text-sm mb-1">
669                          <span>RAM Usage</span>
670                          <span>
671                            {Math.round((parent.usedRam / parent.ram) * 100)}%
672                          </span>
673                        </div>
674                        <Progress value={(parent.usedRam / parent.ram) * 100} />
675                      </div>
676                      <div>
677                        <div className="flex justify-between text-sm mb-1">
678                          <span>Storage Usage</span>
679                          <span>
680                            {Math.round(
681                              (parent.usedStorage / parent.storage) * 100
682                            )}
683                            %
684                          </span>
685                        </div>
686                        <Progress
687                          value={(parent.usedStorage / parent.storage) * 100}
688                        />
689                      </div>
690                    </div>
691
692                    <Separator />
693
694                    <div className="flex justify-between items-center">
695                      <div className="text-sm">
696                        <p>
697                          <strong>{parent.sharedVpsCount}</strong> Shared VPS
698                        </p>
699                        <p>
700                          <strong>{parent.dedicatedVpsCount}</strong> Dedicated
701                          VPS
702                        </p>
703                      </div>
704                      <div className="flex space-x-2">
705                        <Button variant="outline" size="sm">
706                          <Settings className="h-4 w-4" />
707                        </Button>
708                        <Button variant="outline" size="sm">
709                          <Eye className="h-4 w-4" />
710                        </Button>
711                      </div>
712                    </div>
713                  </CardContent>
714                </Card>
715              ))}
716            </div>
717          </TabsContent>
718
719          <TabsContent value="shared" className="space-y-6">
720            <div className="flex items-center justify-between">
721              <h2 className="text-2xl font-bold">Shared VPS Instances</h2>
722              <Dialog
723                open={isCreateSharedOpen}
724                onOpenChange={setIsCreateSharedOpen}
725              >
726                <DialogTrigger asChild>
727                  <Button>
728                    <Plus className="h-4 w-4 mr-2" />
729                    Create Shared VPS
730                  </Button>
731                </DialogTrigger>
732                <DialogContent>
733                  <DialogHeader>
734                    <DialogTitle>Create New Shared VPS</DialogTitle>
735                    <DialogDescription>
736                      Create a shared VPS instance under a parent server
737                    </DialogDescription>
738                  </DialogHeader>
739                  <div className="space-y-4">
740                    <div>
741                      <Label htmlFor="shared-name">Instance Name</Label>
742                      <Input
743                        id="shared-name"
744                        value={newShared.name}
745                        onChange={(e) =>
746                          setNewShared({ ...newShared, name: e.target.value })
747                        }
748                        placeholder="Shared-Store-03"
749                      />
750                    </div>
751                    <div>
752                      <Label htmlFor="shared-parent">Parent Server</Label>
753                      <Select
754                        value={newShared.parentId}
755                        onValueChange={(value) =>
756                          setNewShared({ ...newShared, parentId: value })
757                        }
758                      >
759                        <SelectTrigger>
760                          <SelectValue placeholder="Select parent server" />
761                        </SelectTrigger>
762                        <SelectContent>
763                          {vpsParents.map((parent) => (
764                            <SelectItem key={parent.id} value={parent.id}>
765                              {parent.name} ({parent.region})
766                            </SelectItem>
767                          ))}
768                        </SelectContent>
769                      </Select>
770                    </div>
771                    <div className="grid grid-cols-3 gap-4">
772                      <div>
773                        <Label htmlFor="shared-cpu">CPU Cores</Label>
774                        <Input
775                          id="shared-cpu"
776                          type="number"
777                          value={newShared.cpu}
778                          onChange={(e) =>
779                            setNewShared({
780                              ...newShared,
781                              cpu: parseInt(e.target.value) || 0,
782                            })
783                          }
784                          placeholder="2"
785                        />
786                      </div>
787                      <div>
788                        <Label htmlFor="shared-ram">RAM (GB)</Label>
789                        <Input
790                          id="shared-ram"
791                          type="number"
792                          value={newShared.ram}
793                          onChange={(e) =>
794                            setNewShared({
795                              ...newShared,
796                              ram: parseInt(e.target.value) || 0,
797                            })
798                          }
799                          placeholder="8"
800                        />
801                      </div>
802                      <div>
803                        <Label htmlFor="shared-storage">Storage (GB)</Label>
804                        <Input
805                          id="shared-storage"
806                          type="number"
807                          value={newShared.storage}
808                          onChange={(e) =>
809                            setNewShared({
810                              ...newShared,
811                              storage: parseInt(e.target.value) || 0,
812                            })
813                          }
814                          placeholder="100"
815                        />
816                      </div>
817                    </div>
818                    <div>
819                      <Label htmlFor="shared-customer">Customer Email</Label>
820                      <Input
821                        id="shared-customer"
822                        type="email"
823                        value={newShared.customerEmail}
824                        onChange={(e) =>
825                          setNewShared({
826                            ...newShared,
827                            customerEmail: e.target.value,
828                          })
829                        }
830                        placeholder="customer@example.com"
831                      />
832                    </div>
833                    <div>
834                      <Label htmlFor="shared-domain">Domain (Optional)</Label>
835                      <Input
836                        id="shared-domain"
837                        value={newShared.domain}
838                        onChange={(e) =>
839                          setNewShared({ ...newShared, domain: e.target.value })
840                        }
841                        placeholder="store.example.com"
842                      />
843                    </div>
844                    <Button onClick={createSharedVPS} className="w-full">
845                      Create Shared VPS
846                    </Button>
847                  </div>
848                </DialogContent>
849              </Dialog>
850            </div>
851
852            <Card>
853              <CardContent className="p-0">
854                <Table>
855                  <TableHeader>
856                    <TableRow>
857                      <TableHead>Name</TableHead>
858                      <TableHead>Parent Server</TableHead>
859                      <TableHead>Resources</TableHead>
860                      <TableHead>Customer</TableHead>
861                      <TableHead>Domain</TableHead>
862                      <TableHead>Status</TableHead>
863                      <TableHead>Actions</TableHead>
864                    </TableRow>
865                  </TableHeader>
866                  <TableBody>
867                    {sharedVps.map((vps) => (
868                      <TableRow key={vps.id}>
869                        <TableCell className="font-medium">
870                          {vps.name}
871                        </TableCell>
872                        <TableCell>{vps.parentName}</TableCell>
873                        <TableCell>
874                          <div className="text-sm">
875                            <div>
876                              {vps.cpu} CPU • {vps.ram} GB RAM
877                            </div>
878                            <div className="text-muted-foreground">
879                              {vps.storage} GB Storage
880                            </div>
881                          </div>
882                        </TableCell>
883                        <TableCell>{vps.customerEmail}</TableCell>
884                        <TableCell>{vps.domain || "-"}</TableCell>
885                        <TableCell>
886                          <Badge
887                            variant={
888                              vps.status === "running" ? "default" : "secondary"
889                            }
890                          >
891                            {vps.status}
892                          </Badge>
893                        </TableCell>
894                        <TableCell>
895                          <div className="flex space-x-2">
896                            <Button variant="outline" size="sm">
897                              <Edit className="h-4 w-4" />
898                            </Button>
899                            <Button variant="outline" size="sm">
900                              <Power className="h-4 w-4" />
901                            </Button>
902                            <Button variant="outline" size="sm">
903                              <Trash2 className="h-4 w-4" />
904                            </Button>
905                          </div>
906                        </TableCell>
907                      </TableRow>
908                    ))}
909                  </TableBody>
910                </Table>
911              </CardContent>
912            </Card>
913          </TabsContent>
914
915          <TabsContent value="dedicated" className="space-y-6">
916            <div className="flex items-center justify-between">
917              <h2 className="text-2xl font-bold">Dedicated VPS Instances</h2>
918              <Dialog
919                open={isCreateDedicatedOpen}
920                onOpenChange={setIsCreateDedicatedOpen}
921              >
922                <DialogTrigger asChild>
923                  <Button>
924                    <Plus className="h-4 w-4 mr-2" />
925                    Create Dedicated VPS
926                  </Button>
927                </DialogTrigger>
928                <DialogContent>
929                  <DialogHeader>
930                    <DialogTitle>Create New Dedicated VPS</DialogTitle>
931                    <DialogDescription>
932                      Create a standalone dedicated VPS instance
933                    </DialogDescription>
934                  </DialogHeader>
935                  <div className="space-y-4">
936                    <div>
937                      <Label htmlFor="dedicated-name">Instance Name</Label>
938                      <Input
939                        id="dedicated-name"
940                        value={newDedicated.name}
941                        onChange={(e) =>
942                          setNewDedicated({
943                            ...newDedicated,
944                            name: e.target.value,
945                          })
946                        }
947                        placeholder="Dedicated-Enterprise-02"
948                      />
949                    </div>
950                    <div>
951                      <Label htmlFor="dedicated-region">Region</Label>
952                      <Select
953                        value={newDedicated.region}
954                        onValueChange={(value) =>
955                          setNewDedicated({ ...newDedicated, region: value })
956                        }
957                      >
958                        <SelectTrigger>
959                          <SelectValue placeholder="Select region" />
960                        </SelectTrigger>
961                        <SelectContent>
962                          <SelectItem value="us-east-1">US East 1</SelectItem>
963                          <SelectItem value="us-west-1">US West 1</SelectItem>
964                          <SelectItem value="eu-west-1">EU West 1</SelectItem>
965                          <SelectItem value="ap-southeast-1">
966                            Asia Pacific Southeast 1
967                          </SelectItem>
968                        </SelectContent>
969                      </Select>
970                    </div>
971                    <div className="grid grid-cols-3 gap-4">
972                      <div>
973                        <Label htmlFor="dedicated-cpu">CPU Cores</Label>
974                        <Input
975                          id="dedicated-cpu"
976                          type="number"
977                          value={newDedicated.cpu}
978                          onChange={(e) =>
979                            setNewDedicated({
980                              ...newDedicated,
981                              cpu: parseInt(e.target.value) || 0,
982                            })
983                          }
984                          placeholder="8"
985                        />
986                      </div>
987                      <div>
988                        <Label htmlFor="dedicated-ram">RAM (GB)</Label>
989                        <Input
990                          id="dedicated-ram"
991                          type="number"
992                          value={newDedicated.ram}
993                          onChange={(e) =>
994                            setNewDedicated({
995                              ...newDedicated,
996                              ram: parseInt(e.target.value) || 0,
997                            })
998                          }
999                          placeholder="32"
1000                        />
1001                      </div>
1002                      <div>
1003                        <Label htmlFor="dedicated-storage">Storage (GB)</Label>
1004                        <Input
1005                          id="dedicated-storage"
1006                          type="number"
1007                          value={newDedicated.storage}
1008                          onChange={(e) =>
1009                            setNewDedicated({
1010                              ...newDedicated,
1011                              storage: parseInt(e.target.value) || 0,
1012                            })
1013                          }
1014                          placeholder="500"
1015                        />
1016                      </div>
1017                    </div>
1018                    <div>
1019                      <Label htmlFor="dedicated-customer">Customer Email</Label>
1020                      <Input
1021                        id="dedicated-customer"
1022                        type="email"
1023                        value={newDedicated.customerEmail}
1024                        onChange={(e) =>
1025                          setNewDedicated({
1026                            ...newDedicated,
1027                            customerEmail: e.target.value,
1028                          })
1029                        }
1030                        placeholder="customer@example.com"
1031                      />
1032                    </div>
1033                    <div>
1034                      <Label htmlFor="dedicated-domain">
1035                        Domain (Optional)
1036                      </Label>
1037                      <Input
1038                        id="dedicated-domain"
1039                        value={newDedicated.domain}
1040                        onChange={(e) =>
1041                          setNewDedicated({
1042                            ...newDedicated,
1043                            domain: e.target.value,
1044                          })
1045                        }
1046                        placeholder="enterprise.example.com"
1047                      />
1048                    </div>
1049                    <Button onClick={createDedicatedVPS} className="w-full">
1050                      Create Dedicated VPS
1051                    </Button>
1052                  </div>
1053                </DialogContent>
1054              </Dialog>
1055            </div>
1056
1057            <Card>
1058              <CardContent className="p-0">
1059                <Table>
1060                  <TableHeader>
1061                    <TableRow>
1062                      <TableHead>Name</TableHead>
1063                      <TableHead>Region</TableHead>
1064                      <TableHead>Resources</TableHead>
1065                      <TableHead>Customer</TableHead>
1066                      <TableHead>Domain</TableHead>
1067                      <TableHead>Status</TableHead>
1068                      <TableHead>Actions</TableHead>
1069                    </TableRow>
1070                  </TableHeader>
1071                  <TableBody>
1072                    {dedicatedVps.map((vps) => (
1073                      <TableRow key={vps.id}>
1074                        <TableCell className="font-medium">
1075                          {vps.name}
1076                        </TableCell>
1077                        <TableCell>{vps.region}</TableCell>
1078                        <TableCell>
1079                          <div className="text-sm">
1080                            <div>
1081                              {vps.cpu} CPU • {vps.ram} GB RAM
1082                            </div>
1083                            <div className="text-muted-foreground">
1084                              {vps.storage} GB Storage
1085                            </div>
1086                          </div>
1087                        </TableCell>
1088                        <TableCell>{vps.customerEmail}</TableCell>
1089                        <TableCell>{vps.domain || "-"}</TableCell>
1090                        <TableCell>
1091                          <Badge
1092                            variant={
1093                              vps.status === "running" ? "default" : "secondary"
1094                            }
1095                          >
1096                            {vps.status}
1097                          </Badge>
1098                        </TableCell>
1099                        <TableCell>
1100                          <div className="flex space-x-2">
1101                            <Button variant="outline" size="sm">
1102                              <Edit className="h-4 w-4" />
1103                            </Button>
1104                            <Button variant="outline" size="sm">
1105                              <Power className="h-4 w-4" />
1106                            </Button>
1107                            <Button variant="outline" size="sm">
1108                              <Trash2 className="h-4 w-4" />
1109                            </Button>
1110                          </div>
1111                        </TableCell>
1112                      </TableRow>
1113                    ))}
1114                  </TableBody>
1115                </Table>
1116              </CardContent>
1117            </Card>
1118          </TabsContent>
1119        </Tabs>
1120      </div>
1121    </div>
1122  );
1123};
1124
1125export default VPSAdminDashboard;

Dependencies

External Libraries

lucide-reactreact

Shadcn/UI Components

badgebuttoncarddialoginputlabelprogressselectseparatorswitchtabletabs

LICENSE

MIT License

Copyright (c) 2025 Aldhaneka

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Shadcn Vaults Project (CC BY-NC 4.0 with Internal Use Exception)

All user-submitted components in the '/blocks' directory are licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0),
with the following clarification and exception:

You are free to:
- Share — copy and redistribute the material in any medium or format
- Adapt — remix, transform, and build upon the material

Under these conditions:
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made.
- NonCommercial — You may NOT use the material for commercial redistribution, resale, or monetization.

🚫 You MAY NOT:
- Sell or redistribute the components individually or as part of a product (e.g. a UI kit, template marketplace, SaaS component library)
- Offer the components or derivative works in any paid tool, theme pack, or design system

✅ You MAY:
- Use the components in internal company tools, dashboards, or applications that are not sold as products
- Remix or adapt components for private or enterprise projects
- Use them in open-source non-commercial projects

This license encourages sharing, learning, and internal innovation — but prohibits using these components as a basis for monetized products.

Full license text: https://creativecommons.org/licenses/by-nc/4.0/

By submitting a component, contributors agree to these terms.

Contributors

Ramiro Godoy@milogodoy

Review Form Block

Aldhaneka@Aldhanekaa

Project Creator

For questions about licensing, please contact the project maintainers.