Preview
Full width desktop view
Code
monitoring-8.tsx
1"use client";
2
3import React, { useState, useEffect } from "react";
4import {
5 Server,
6 Plus,
7 Settings,
8 Monitor,
9 Database,
10 Globe,
11 Cpu,
12 HardDrive,
13 MemoryStick,
14 Activity,
15 Users,
16 ChevronRight,
17 ChevronDown,
18 Edit,
19 Trash2,
20 Play,
21 Square,
22 RotateCcw,
23 AlertCircle,
24 CheckCircle,
25 Clock,
26 Search,
27 Filter,
28 MoreVertical,
29 MapPin,
30 Zap,
31 Shield,
32 Menu,
33 X,
34 ChevronLeft,
35 Home,
36 BarChart3,
37} from "lucide-react";
38import { motion, AnimatePresence } from "framer-motion";
39import {
40 Card,
41 CardContent,
42 CardDescription,
43 CardHeader,
44 CardTitle,
45} from "@/components/ui/card";
46import { Button } from "@/components/ui/button";
47import { Input } from "@/components/ui/input";
48import { Label } from "@/components/ui/label";
49import {
50 Select,
51 SelectContent,
52 SelectItem,
53 SelectTrigger,
54 SelectValue,
55} from "@/components/ui/select";
56import { Badge } from "@/components/ui/badge";
57import {
58 Dialog,
59 DialogContent,
60 DialogDescription,
61 DialogHeader,
62 DialogTitle,
63 DialogTrigger,
64} from "@/components/ui/dialog";
65import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
66import { Progress } from "@/components/ui/progress";
67import { Separator } from "@/components/ui/separator";
68import { Switch } from "@/components/ui/switch";
69import { Textarea } from "@/components/ui/textarea";
70
71interface VPSParent {
72 id: string;
73 name: string;
74 region: string;
75 cpu: number;
76 ram: number;
77 storage: number;
78 status: "online" | "offline" | "maintenance";
79 ipAddress: string;
80 createdAt: string;
81 sharedVPS: SharedVPS[];
82 dedicatedVPS: DedicatedVPS[];
83 totalSharedInstances: number;
84 usedResources: {
85 cpu: number;
86 ram: number;
87 storage: number;
88 };
89}
90
91interface SharedVPS {
92 id: string;
93 name: string;
94 parentId: string;
95 customerId: string;
96 customerName: string;
97 allocatedCpu: number;
98 allocatedRam: number;
99 allocatedStorage: number;
100 status: "running" | "stopped" | "suspended";
101 domain?: string;
102 createdAt: string;
103}
104
105interface DedicatedVPS {
106 id: string;
107 name: string;
108 parentId: string;
109 customerId: string;
110 customerName: string;
111 cpu: number;
112 ram: number;
113 storage: number;
114 status: "running" | "stopped" | "suspended";
115 ipAddress: string;
116 domain?: string;
117 createdAt: string;
118}
119
120const VPSAdminDashboard = () => {
121 const [sidebarOpen, setSidebarOpen] = useState(false);
122 const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
123 const [activeTab, setActiveTab] = useState("overview");
124 const [searchTerm, setSearchTerm] = useState("");
125 const [selectedRegion, setSelectedRegion] = useState("all");
126 const [expandedParents, setExpandedParents] = useState<Set<string>>(
127 new Set()
128 );
129
130 // Sample data
131 const [vpsParents, setVpsParents] = useState<VPSParent[]>([
132 {
133 id: "parent-1",
134 name: "US-East-Primary",
135 region: "us-east-1",
136 cpu: 32,
137 ram: 128,
138 storage: 2000,
139 status: "online",
140 ipAddress: "192.168.1.100",
141 createdAt: "2024-01-15",
142 totalSharedInstances: 8,
143 usedResources: { cpu: 24, ram: 96, storage: 1200 },
144 sharedVPS: [
145 {
146 id: "shared-1",
147 name: "Store-Alpha",
148 parentId: "parent-1",
149 customerId: "cust-1",
150 customerName: "John Doe",
151 allocatedCpu: 4,
152 allocatedRam: 8,
153 allocatedStorage: 100,
154 status: "running",
155 domain: "store-alpha.com",
156 createdAt: "2024-01-20",
157 },
158 {
159 id: "shared-2",
160 name: "Store-Beta",
161 parentId: "parent-1",
162 customerId: "cust-2",
163 customerName: "Jane Smith",
164 allocatedCpu: 2,
165 allocatedRam: 4,
166 allocatedStorage: 50,
167 status: "running",
168 domain: "store-beta.com",
169 createdAt: "2024-01-22",
170 },
171 ],
172 dedicatedVPS: [
173 {
174 id: "dedicated-1",
175 name: "Enterprise-Store",
176 parentId: "parent-1",
177 customerId: "cust-3",
178 customerName: "Acme Corp",
179 cpu: 16,
180 ram: 64,
181 storage: 500,
182 status: "running",
183 ipAddress: "192.168.1.101",
184 domain: "enterprise-store.com",
185 createdAt: "2024-01-18",
186 },
187 ],
188 },
189 {
190 id: "parent-2",
191 name: "EU-West-Primary",
192 region: "eu-west-1",
193 cpu: 24,
194 ram: 96,
195 storage: 1500,
196 status: "online",
197 ipAddress: "192.168.2.100",
198 createdAt: "2024-01-10",
199 totalSharedInstances: 6,
200 usedResources: { cpu: 18, ram: 72, storage: 900 },
201 sharedVPS: [],
202 dedicatedVPS: [],
203 },
204 ]);
205
206 const [newVPSParent, setNewVPSParent] = useState({
207 name: "",
208 region: "",
209 cpu: 0,
210 ram: 0,
211 storage: 0,
212 ipAddress: "",
213 });
214
215 const [newSharedVPS, setNewSharedVPS] = useState({
216 name: "",
217 parentId: "",
218 customerId: "",
219 customerName: "",
220 allocatedCpu: 0,
221 allocatedRam: 0,
222 allocatedStorage: 0,
223 domain: "",
224 });
225
226 const [newDedicatedVPS, setNewDedicatedVPS] = useState({
227 name: "",
228 parentId: "",
229 customerId: "",
230 customerName: "",
231 cpu: 0,
232 ram: 0,
233 storage: 0,
234 ipAddress: "",
235 domain: "",
236 });
237
238 const menuItems = [
239 { name: "Overview", icon: Home, key: "overview" },
240 { name: "VPS Parents", icon: Server, key: "parents" },
241 { name: "Shared VPS", icon: Users, key: "shared" },
242 { name: "Dedicated VPS", icon: Database, key: "dedicated" },
243 { name: "Monitoring", icon: Activity, key: "monitoring" },
244 { name: "Settings", icon: Settings, key: "settings" },
245 ];
246
247 const regions = [
248 { value: "us-east-1", label: "US East (N. Virginia)" },
249 { value: "us-west-2", label: "US West (Oregon)" },
250 { value: "eu-west-1", label: "EU West (Ireland)" },
251 { value: "ap-southeast-1", label: "Asia Pacific (Singapore)" },
252 ];
253
254 const getStatusColor = (status: string) => {
255 switch (status) {
256 case "online":
257 case "running":
258 return "bg-green-500";
259 case "offline":
260 case "stopped":
261 return "bg-red-500";
262 case "maintenance":
263 case "suspended":
264 return "bg-yellow-500";
265 default:
266 return "bg-gray-500";
267 }
268 };
269
270 const getStatusIcon = (status: string) => {
271 switch (status) {
272 case "online":
273 case "running":
274 return <CheckCircle className="h-4 w-4 text-green-500" />;
275 case "offline":
276 case "stopped":
277 return <AlertCircle className="h-4 w-4 text-red-500" />;
278 case "maintenance":
279 case "suspended":
280 return <Clock className="h-4 w-4 text-yellow-500" />;
281 default:
282 return <AlertCircle className="h-4 w-4 text-gray-500" />;
283 }
284 };
285
286 const toggleParentExpansion = (parentId: string) => {
287 setExpandedParents((prev) => {
288 const newSet = new Set(prev);
289 if (newSet.has(parentId)) {
290 newSet.delete(parentId);
291 } else {
292 newSet.add(parentId);
293 }
294 return newSet;
295 });
296 };
297
298 const createVPSParent = () => {
299 const newParent: VPSParent = {
300 id: `parent-${Date.now()}`,
301 ...newVPSParent,
302 status: "online",
303 createdAt: new Date().toISOString().split("T")[0],
304 sharedVPS: [],
305 dedicatedVPS: [],
306 totalSharedInstances: 0,
307 usedResources: { cpu: 0, ram: 0, storage: 0 },
308 };
309 setVpsParents([...vpsParents, newParent]);
310 setNewVPSParent({
311 name: "",
312 region: "",
313 cpu: 0,
314 ram: 0,
315 storage: 0,
316 ipAddress: "",
317 });
318 };
319
320 const createSharedVPS = () => {
321 const parentIndex = vpsParents.findIndex(
322 (p) => p.id === newSharedVPS.parentId
323 );
324 if (parentIndex !== -1) {
325 const newShared: SharedVPS = {
326 id: `shared-${Date.now()}`,
327 ...newSharedVPS,
328 status: "running",
329 createdAt: new Date().toISOString().split("T")[0],
330 };
331
332 const updatedParents = [...vpsParents];
333 updatedParents[parentIndex].sharedVPS.push(newShared);
334 updatedParents[parentIndex].totalSharedInstances += 1;
335 updatedParents[parentIndex].usedResources.cpu +=
336 newSharedVPS.allocatedCpu;
337 updatedParents[parentIndex].usedResources.ram +=
338 newSharedVPS.allocatedRam;
339 updatedParents[parentIndex].usedResources.storage +=
340 newSharedVPS.allocatedStorage;
341
342 setVpsParents(updatedParents);
343 setNewSharedVPS({
344 name: "",
345 parentId: "",
346 customerId: "",
347 customerName: "",
348 allocatedCpu: 0,
349 allocatedRam: 0,
350 allocatedStorage: 0,
351 domain: "",
352 });
353 }
354 };
355
356 const createDedicatedVPS = () => {
357 const parentIndex = vpsParents.findIndex(
358 (p) => p.id === newDedicatedVPS.parentId
359 );
360 if (parentIndex !== -1) {
361 const newDedicated: DedicatedVPS = {
362 id: `dedicated-${Date.now()}`,
363 ...newDedicatedVPS,
364 status: "running",
365 createdAt: new Date().toISOString().split("T")[0],
366 };
367
368 const updatedParents = [...vpsParents];
369 updatedParents[parentIndex].dedicatedVPS.push(newDedicated);
370
371 setVpsParents(updatedParents);
372 setNewDedicatedVPS({
373 name: "",
374 parentId: "",
375 customerId: "",
376 customerName: "",
377 cpu: 0,
378 ram: 0,
379 storage: 0,
380 ipAddress: "",
381 domain: "",
382 });
383 }
384 };
385
386 const filteredParents = vpsParents.filter((parent) => {
387 const matchesSearch = parent.name
388 .toLowerCase()
389 .includes(searchTerm.toLowerCase());
390 const matchesRegion =
391 selectedRegion === "all" || parent.region === selectedRegion;
392 return matchesSearch && matchesRegion;
393 });
394
395 const totalStats = {
396 totalParents: vpsParents.length,
397 totalShared: vpsParents.reduce(
398 (sum, parent) => sum + parent.sharedVPS.length,
399 0
400 ),
401 totalDedicated: vpsParents.reduce(
402 (sum, parent) => sum + parent.dedicatedVPS.length,
403 0
404 ),
405 onlineParents: vpsParents.filter((p) => p.status === "online").length,
406 };
407
408 useEffect(() => {
409 const handleResize = () => {
410 if (window.innerWidth >= 768) {
411 setSidebarOpen(true);
412 } else {
413 setSidebarOpen(false);
414 }
415 };
416
417 handleResize();
418 window.addEventListener("resize", handleResize);
419 return () => window.removeEventListener("resize", handleResize);
420 }, []);
421
422 return (
423 <div className="flex h-screen bg-background">
424 {/* Mobile hamburger button */}
425 <button
426 onClick={() => setSidebarOpen(!sidebarOpen)}
427 className="fixed top-6 left-6 z-50 p-3 rounded-lg bg-white shadow-md border border-border md:hidden hover:bg-accent transition-all duration-200"
428 >
429 {sidebarOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
430 </button>
431
432 {/* Mobile overlay */}
433 {sidebarOpen && (
434 <div
435 className="fixed inset-0 bg-black/40 backdrop-blur-sm z-30 md:hidden transition-opacity duration-300"
436 onClick={() => setSidebarOpen(false)}
437 />
438 )}
439
440 {/* Sidebar */}
441 <div
442 className={`
443 fixed top-0 left-0 h-full bg-card border-r border-border z-40 transition-all duration-300 ease-in-out flex flex-col
444 ${sidebarOpen ? "translate-x-0" : "-translate-x-full"}
445 ${sidebarCollapsed ? "w-20" : "w-72"}
446 md:translate-x-0 md:static md:z-auto
447 `}
448 >
449 {/* Header */}
450 <div className="flex items-center justify-between p-5 border-b border-border">
451 {!sidebarCollapsed && (
452 <div className="flex items-center space-x-3">
453 <div className="w-10 h-10 bg-primary rounded-lg flex items-center justify-center">
454 <Server className="h-6 w-6 text-primary-foreground" />
455 </div>
456 <div>
457 <h1 className="text-lg font-bold text-foreground">VPS Admin</h1>
458 <p className="text-xs text-muted-foreground">
459 Management Console
460 </p>
461 </div>
462 </div>
463 )}
464
465 <button
466 onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
467 className="hidden md:flex p-1.5 rounded-md hover:bg-accent transition-all duration-200"
468 >
469 {sidebarCollapsed ? (
470 <ChevronRight className="h-4 w-4 text-muted-foreground" />
471 ) : (
472 <ChevronLeft className="h-4 w-4 text-muted-foreground" />
473 )}
474 </button>
475 </div>
476
477 {/* Navigation */}
478 <nav className="flex-1 p-4 space-y-2">
479 {menuItems.map((item) => {
480 const Icon = item.icon;
481 const isActive = activeTab === item.key;
482
483 return (
484 <button
485 key={item.key}
486 onClick={() => {
487 setActiveTab(item.key);
488 if (window.innerWidth < 768) setSidebarOpen(false);
489 }}
490 className={`
491 w-full flex items-center space-x-3 px-3 py-2.5 rounded-lg transition-all duration-200 group
492 ${
493 isActive
494 ? "bg-primary text-primary-foreground"
495 : "text-muted-foreground hover:bg-accent hover:text-foreground"
496 }
497 ${sidebarCollapsed ? "justify-center px-2" : ""}
498 `}
499 title={sidebarCollapsed ? item.name : undefined}
500 >
501 <Icon className="h-5 w-5 flex-shrink-0" />
502 {!sidebarCollapsed && (
503 <span className="font-medium">{item.name}</span>
504 )}
505 </button>
506 );
507 })}
508 </nav>
509 </div>
510
511 {/* Main Content */}
512 <div
513 className={`flex-1 flex flex-col transition-all duration-300 ${
514 sidebarCollapsed ? "md:ml-20" : "md:ml-72"
515 }`}
516 >
517 {/* Header */}
518 <header className="bg-card border-b border-border p-6 ml-16 md:ml-0">
519 <div className="flex items-center justify-between">
520 <div>
521 <h1 className="text-2xl font-bold text-foreground">
522 VPS Management Dashboard
523 </h1>
524 <p className="text-muted-foreground">
525 Manage and monitor your VPS infrastructure
526 </p>
527 </div>
528 <div className="flex items-center space-x-4">
529 <div className="relative">
530 <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
531 <Input
532 placeholder="Search VPS..."
533 value={searchTerm}
534 onChange={(e) => setSearchTerm(e.target.value)}
535 className="pl-10 w-64"
536 />
537 </div>
538 <Select value={selectedRegion} onValueChange={setSelectedRegion}>
539 <SelectTrigger className="w-48">
540 <SelectValue placeholder="Filter by region" />
541 </SelectTrigger>
542 <SelectContent>
543 <SelectItem value="all">All Regions</SelectItem>
544 {regions.map((region) => (
545 <SelectItem key={region.value} value={region.value}>
546 {region.label}
547 </SelectItem>
548 ))}
549 </SelectContent>
550 </Select>
551 </div>
552 </div>
553 </header>
554
555 {/* Content */}
556 <main className="flex-1 p-6 ml-16 md:ml-0 overflow-auto">
557 <Tabs value={activeTab} onValueChange={setActiveTab}>
558 {/* Overview Tab */}
559 <TabsContent value="overview" className="space-y-6">
560 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
561 <Card>
562 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
563 <CardTitle className="text-sm font-medium">
564 VPS Parents
565 </CardTitle>
566 <Server className="h-4 w-4 text-muted-foreground" />
567 </CardHeader>
568 <CardContent>
569 <div className="text-2xl font-bold">
570 {totalStats.totalParents}
571 </div>
572 <p className="text-xs text-muted-foreground">
573 {totalStats.onlineParents} online
574 </p>
575 </CardContent>
576 </Card>
577
578 <Card>
579 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
580 <CardTitle className="text-sm font-medium">
581 Shared VPS
582 </CardTitle>
583 <Users className="h-4 w-4 text-muted-foreground" />
584 </CardHeader>
585 <CardContent>
586 <div className="text-2xl font-bold">
587 {totalStats.totalShared}
588 </div>
589 <p className="text-xs text-muted-foreground">
590 Active instances
591 </p>
592 </CardContent>
593 </Card>
594
595 <Card>
596 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
597 <CardTitle className="text-sm font-medium">
598 Dedicated VPS
599 </CardTitle>
600 <Database className="h-4 w-4 text-muted-foreground" />
601 </CardHeader>
602 <CardContent>
603 <div className="text-2xl font-bold">
604 {totalStats.totalDedicated}
605 </div>
606 <p className="text-xs text-muted-foreground">
607 Standalone instances
608 </p>
609 </CardContent>
610 </Card>
611
612 <Card>
613 <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
614 <CardTitle className="text-sm font-medium">
615 Total Customers
616 </CardTitle>
617 <Globe className="h-4 w-4 text-muted-foreground" />
618 </CardHeader>
619 <CardContent>
620 <div className="text-2xl font-bold">
621 {
622 new Set([
623 ...vpsParents.flatMap((p) =>
624 p.sharedVPS.map((s) => s.customerId)
625 ),
626 ...vpsParents.flatMap((p) =>
627 p.dedicatedVPS.map((d) => d.customerId)
628 ),
629 ]).size
630 }
631 </div>
632 <p className="text-xs text-muted-foreground">
633 Unique customers
634 </p>
635 </CardContent>
636 </Card>
637 </div>
638
639 {/* Recent Activity */}
640 <Card>
641 <CardHeader>
642 <CardTitle>Recent Activity</CardTitle>
643 <CardDescription>
644 Latest VPS management activities
645 </CardDescription>
646 </CardHeader>
647 <CardContent>
648 <div className="space-y-4">
649 {[
650 {
651 action: "Created new VPS Parent",
652 target: "US-East-Primary",
653 time: "2 hours ago",
654 type: "create",
655 },
656 {
657 action: "Deployed Shared VPS",
658 target: "Store-Alpha",
659 time: "4 hours ago",
660 type: "deploy",
661 },
662 {
663 action: "Updated Dedicated VPS",
664 target: "Enterprise-Store",
665 time: "6 hours ago",
666 type: "update",
667 },
668 {
669 action: "Maintenance completed",
670 target: "EU-West-Primary",
671 time: "1 day ago",
672 type: "maintenance",
673 },
674 ].map((activity, index) => (
675 <div key={index} className="flex items-center space-x-4">
676 <div
677 className={`w-2 h-2 rounded-full ${
678 activity.type === "create"
679 ? "bg-green-500"
680 : activity.type === "deploy"
681 ? "bg-blue-500"
682 : activity.type === "update"
683 ? "bg-yellow-500"
684 : "bg-purple-500"
685 }`}
686 />
687 <div className="flex-1">
688 <p className="text-sm font-medium">
689 {activity.action}
690 </p>
691 <p className="text-xs text-muted-foreground">
692 {activity.target}
693 </p>
694 </div>
695 <p className="text-xs text-muted-foreground">
696 {activity.time}
697 </p>
698 </div>
699 ))}
700 </div>
701 </CardContent>
702 </Card>
703 </TabsContent>
704
705 {/* VPS Parents Tab */}
706 <TabsContent value="parents" className="space-y-6">
707 <div className="flex justify-between items-center">
708 <h2 className="text-xl font-semibold">VPS Parents</h2>
709 <Dialog>
710 <DialogTrigger asChild>
711 <Button>
712 <Plus className="h-4 w-4 mr-2" />
713 Add VPS Parent
714 </Button>
715 </DialogTrigger>
716 <DialogContent className="sm:max-w-md">
717 <DialogHeader>
718 <DialogTitle>Create New VPS Parent</DialogTitle>
719 <DialogDescription>
720 Set up a new VPS parent server with specified resources.
721 </DialogDescription>
722 </DialogHeader>
723 <div className="space-y-4">
724 <div>
725 <Label htmlFor="parent-name">Server Name</Label>
726 <Input
727 id="parent-name"
728 value={newVPSParent.name}
729 onChange={(e) =>
730 setNewVPSParent({
731 ...newVPSParent,
732 name: e.target.value,
733 })
734 }
735 placeholder="e.g., US-East-Primary"
736 />
737 </div>
738 <div>
739 <Label htmlFor="parent-region">Region</Label>
740 <Select
741 value={newVPSParent.region}
742 onValueChange={(value) =>
743 setNewVPSParent({ ...newVPSParent, region: value })
744 }
745 >
746 <SelectTrigger>
747 <SelectValue placeholder="Select region" />
748 </SelectTrigger>
749 <SelectContent>
750 {regions.map((region) => (
751 <SelectItem
752 key={region.value}
753 value={region.value}
754 >
755 {region.label}
756 </SelectItem>
757 ))}
758 </SelectContent>
759 </Select>
760 </div>
761 <div className="grid grid-cols-3 gap-4">
762 <div>
763 <Label htmlFor="parent-cpu">CPU Cores</Label>
764 <Input
765 id="parent-cpu"
766 type="number"
767 value={newVPSParent.cpu}
768 onChange={(e) =>
769 setNewVPSParent({
770 ...newVPSParent,
771 cpu: parseInt(e.target.value) || 0,
772 })
773 }
774 placeholder="32"
775 />
776 </div>
777 <div>
778 <Label htmlFor="parent-ram">RAM (GB)</Label>
779 <Input
780 id="parent-ram"
781 type="number"
782 value={newVPSParent.ram}
783 onChange={(e) =>
784 setNewVPSParent({
785 ...newVPSParent,
786 ram: parseInt(e.target.value) || 0,
787 })
788 }
789 placeholder="128"
790 />
791 </div>
792 <div>
793 <Label htmlFor="parent-storage">Storage (GB)</Label>
794 <Input
795 id="parent-storage"
796 type="number"
797 value={newVPSParent.storage}
798 onChange={(e) =>
799 setNewVPSParent({
800 ...newVPSParent,
801 storage: parseInt(e.target.value) || 0,
802 })
803 }
804 placeholder="2000"
805 />
806 </div>
807 </div>
808 <div>
809 <Label htmlFor="parent-ip">IP Address</Label>
810 <Input
811 id="parent-ip"
812 value={newVPSParent.ipAddress}
813 onChange={(e) =>
814 setNewVPSParent({
815 ...newVPSParent,
816 ipAddress: e.target.value,
817 })
818 }
819 placeholder="192.168.1.100"
820 />
821 </div>
822 <Button onClick={createVPSParent} className="w-full">
823 Create VPS Parent
824 </Button>
825 </div>
826 </DialogContent>
827 </Dialog>
828 </div>
829
830 <div className="space-y-4">
831 {filteredParents.map((parent) => (
832 <Card key={parent.id}>
833 <CardHeader>
834 <div className="flex items-center justify-between">
835 <div className="flex items-center space-x-3">
836 <button
837 onClick={() => toggleParentExpansion(parent.id)}
838 className="p-1 hover:bg-accent rounded"
839 >
840 {expandedParents.has(parent.id) ? (
841 <ChevronDown className="h-4 w-4" />
842 ) : (
843 <ChevronRight className="h-4 w-4" />
844 )}
845 </button>
846 <div>
847 <CardTitle className="flex items-center space-x-2">
848 <span>{parent.name}</span>
849 <Badge
850 variant={
851 parent.status === "online"
852 ? "default"
853 : "destructive"
854 }
855 >
856 {parent.status}
857 </Badge>
858 </CardTitle>
859 <CardDescription className="flex items-center space-x-4">
860 <span className="flex items-center space-x-1">
861 <MapPin className="h-3 w-3" />
862 <span>
863 {
864 regions.find(
865 (r) => r.value === parent.region
866 )?.label
867 }
868 </span>
869 </span>
870 <span>{parent.ipAddress}</span>
871 </CardDescription>
872 </div>
873 </div>
874 <div className="flex items-center space-x-2">
875 <Button variant="outline" size="sm">
876 <Settings className="h-4 w-4" />
877 </Button>
878 <Button variant="outline" size="sm">
879 <MoreVertical className="h-4 w-4" />
880 </Button>
881 </div>
882 </div>
883 </CardHeader>
884 <CardContent>
885 <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
886 <div className="space-y-2">
887 <div className="flex items-center justify-between">
888 <span className="text-sm text-muted-foreground">
889 CPU Usage
890 </span>
891 <span className="text-sm font-medium">
892 {parent.usedResources.cpu}/{parent.cpu} cores
893 </span>
894 </div>
895 <Progress
896 value={
897 (parent.usedResources.cpu / parent.cpu) * 100
898 }
899 />
900 </div>
901 <div className="space-y-2">
902 <div className="flex items-center justify-between">
903 <span className="text-sm text-muted-foreground">
904 RAM Usage
905 </span>
906 <span className="text-sm font-medium">
907 {parent.usedResources.ram}/{parent.ram} GB
908 </span>
909 </div>
910 <Progress
911 value={
912 (parent.usedResources.ram / parent.ram) * 100
913 }
914 />
915 </div>
916 <div className="space-y-2">
917 <div className="flex items-center justify-between">
918 <span className="text-sm text-muted-foreground">
919 Storage Usage
920 </span>
921 <span className="text-sm font-medium">
922 {parent.usedResources.storage}/{parent.storage} GB
923 </span>
924 </div>
925 <Progress
926 value={
927 (parent.usedResources.storage / parent.storage) *
928 100
929 }
930 />
931 </div>
932 </div>
933
934 <AnimatePresence>
935 {expandedParents.has(parent.id) && (
936 <motion.div
937 initial={{ height: 0, opacity: 0 }}
938 animate={{ height: "auto", opacity: 1 }}
939 exit={{ height: 0, opacity: 0 }}
940 transition={{ duration: 0.3 }}
941 className="overflow-hidden"
942 >
943 <Separator className="my-4" />
944 <div className="space-y-4">
945 <div className="flex items-center justify-between">
946 <h4 className="font-medium">
947 Shared VPS Instances (
948 {parent.sharedVPS.length})
949 </h4>
950 <Dialog>
951 <DialogTrigger asChild>
952 <Button variant="outline" size="sm">
953 <Plus className="h-4 w-4 mr-1" />
954 Add Shared VPS
955 </Button>
956 </DialogTrigger>
957 <DialogContent>
958 <DialogHeader>
959 <DialogTitle>
960 Create Shared VPS
961 </DialogTitle>
962 <DialogDescription>
963 Create a new shared VPS instance under{" "}
964 {parent.name}
965 </DialogDescription>
966 </DialogHeader>
967 <div className="space-y-4">
968 <div>
969 <Label>VPS Name</Label>
970 <Input
971 value={newSharedVPS.name}
972 onChange={(e) =>
973 setNewSharedVPS({
974 ...newSharedVPS,
975 name: e.target.value,
976 parentId: parent.id,
977 })
978 }
979 placeholder="Store-Gamma"
980 />
981 </div>
982 <div className="grid grid-cols-2 gap-4">
983 <div>
984 <Label>Customer ID</Label>
985 <Input
986 value={newSharedVPS.customerId}
987 onChange={(e) =>
988 setNewSharedVPS({
989 ...newSharedVPS,
990 customerId: e.target.value,
991 })
992 }
993 placeholder="cust-123"
994 />
995 </div>
996 <div>
997 <Label>Customer Name</Label>
998 <Input
999 value={newSharedVPS.customerName}
1000 onChange={(e) =>
1001 setNewSharedVPS({
1002 ...newSharedVPS,
1003 customerName: e.target.value,
1004 })
1005 }
1006 placeholder="John Doe"
1007 />
1008 </div>
1009 </div>
1010 <div className="grid grid-cols-3 gap-4">
1011 <div>
1012 <Label>CPU Cores</Label>
1013 <Input
1014 type="number"
1015 value={newSharedVPS.allocatedCpu}
1016 onChange={(e) =>
1017 setNewSharedVPS({
1018 ...newSharedVPS,
1019 allocatedCpu:
1020 parseInt(e.target.value) || 0,
1021 })
1022 }
1023 placeholder="2"
1024 />
1025 </div>
1026 <div>
1027 <Label>RAM (GB)</Label>
1028 <Input
1029 type="number"
1030 value={newSharedVPS.allocatedRam}
1031 onChange={(e) =>
1032 setNewSharedVPS({
1033 ...newSharedVPS,
1034 allocatedRam:
1035 parseInt(e.target.value) || 0,
1036 })
1037 }
1038 placeholder="4"
1039 />
1040 </div>
1041 <div>
1042 <Label>Storage (GB)</Label>
1043 <Input
1044 type="number"
1045 value={
1046 newSharedVPS.allocatedStorage
1047 }
1048 onChange={(e) =>
1049 setNewSharedVPS({
1050 ...newSharedVPS,
1051 allocatedStorage:
1052 parseInt(e.target.value) || 0,
1053 })
1054 }
1055 placeholder="50"
1056 />
1057 </div>
1058 </div>
1059 <div>
1060 <Label>Domain (Optional)</Label>
1061 <Input
1062 value={newSharedVPS.domain}
1063 onChange={(e) =>
1064 setNewSharedVPS({
1065 ...newSharedVPS,
1066 domain: e.target.value,
1067 })
1068 }
1069 placeholder="store-gamma.com"
1070 />
1071 </div>
1072 <Button
1073 onClick={createSharedVPS}
1074 className="w-full"
1075 >
1076 Create Shared VPS
1077 </Button>
1078 </div>
1079 </DialogContent>
1080 </Dialog>
1081 </div>
1082
1083 {parent.sharedVPS.length > 0 ? (
1084 <div className="grid gap-2">
1085 {parent.sharedVPS.map((shared) => (
1086 <div
1087 key={shared.id}
1088 className="flex items-center justify-between p-3 bg-accent/50 rounded-lg"
1089 >
1090 <div className="flex items-center space-x-3">
1091 <div
1092 className={`w-2 h-2 rounded-full ${getStatusColor(
1093 shared.status
1094 )}`}
1095 />
1096 <div>
1097 <p className="font-medium">
1098 {shared.name}
1099 </p>
1100 <p className="text-xs text-muted-foreground">
1101 {shared.customerName} •{" "}
1102 {shared.allocatedCpu}C/
1103 {shared.allocatedRam}GB/
1104 {shared.allocatedStorage}GB
1105 </p>
1106 </div>
1107 </div>
1108 <div className="flex items-center space-x-2">
1109 <Badge variant="outline">
1110 {shared.status}
1111 </Badge>
1112 <Button variant="ghost" size="sm">
1113 <Settings className="h-3 w-3" />
1114 </Button>
1115 </div>
1116 </div>
1117 ))}
1118 </div>
1119 ) : (
1120 <p className="text-sm text-muted-foreground text-center py-4">
1121 No shared VPS instances
1122 </p>
1123 )}
1124
1125 <div className="flex items-center justify-between">
1126 <h4 className="font-medium">
1127 Dedicated VPS Instances (
1128 {parent.dedicatedVPS.length})
1129 </h4>
1130 <Dialog>
1131 <DialogTrigger asChild>
1132 <Button variant="outline" size="sm">
1133 <Plus className="h-4 w-4 mr-1" />
1134 Add Dedicated VPS
1135 </Button>
1136 </DialogTrigger>
1137 <DialogContent>
1138 <DialogHeader>
1139 <DialogTitle>
1140 Create Dedicated VPS
1141 </DialogTitle>
1142 <DialogDescription>
1143 Create a new dedicated VPS instance
1144 under {parent.name}
1145 </DialogDescription>
1146 </DialogHeader>
1147 <div className="space-y-4">
1148 <div>
1149 <Label>VPS Name</Label>
1150 <Input
1151 value={newDedicatedVPS.name}
1152 onChange={(e) =>
1153 setNewDedicatedVPS({
1154 ...newDedicatedVPS,
1155 name: e.target.value,
1156 parentId: parent.id,
1157 })
1158 }
1159 placeholder="Enterprise-Store-2"
1160 />
1161 </div>
1162 <div className="grid grid-cols-2 gap-4">
1163 <div>
1164 <Label>Customer ID</Label>
1165 <Input
1166 value={newDedicatedVPS.customerId}
1167 onChange={(e) =>
1168 setNewDedicatedVPS({
1169 ...newDedicatedVPS,
1170 customerId: e.target.value,
1171 })
1172 }
1173 placeholder="cust-456"
1174 />
1175 </div>
1176 <div>
1177 <Label>Customer Name</Label>
1178 <Input
1179 value={newDedicatedVPS.customerName}
1180 onChange={(e) =>
1181 setNewDedicatedVPS({
1182 ...newDedicatedVPS,
1183 customerName: e.target.value,
1184 })
1185 }
1186 placeholder="Acme Corp"
1187 />
1188 </div>
1189 </div>
1190 <div className="grid grid-cols-3 gap-4">
1191 <div>
1192 <Label>CPU Cores</Label>
1193 <Input
1194 type="number"
1195 value={newDedicatedVPS.cpu}
1196 onChange={(e) =>
1197 setNewDedicatedVPS({
1198 ...newDedicatedVPS,
1199 cpu:
1200 parseInt(e.target.value) || 0,
1201 })
1202 }
1203 placeholder="8"
1204 />
1205 </div>
1206 <div>
1207 <Label>RAM (GB)</Label>
1208 <Input
1209 type="number"
1210 value={newDedicatedVPS.ram}
1211 onChange={(e) =>
1212 setNewDedicatedVPS({
1213 ...newDedicatedVPS,
1214 ram:
1215 parseInt(e.target.value) || 0,
1216 })
1217 }
1218 placeholder="32"
1219 />
1220 </div>
1221 <div>
1222 <Label>Storage (GB)</Label>
1223 <Input
1224 type="number"
1225 value={newDedicatedVPS.storage}
1226 onChange={(e) =>
1227 setNewDedicatedVPS({
1228 ...newDedicatedVPS,
1229 storage:
1230 parseInt(e.target.value) || 0,
1231 })
1232 }
1233 placeholder="500"
1234 />
1235 </div>
1236 </div>
1237 <div className="grid grid-cols-2 gap-4">
1238 <div>
1239 <Label>IP Address</Label>
1240 <Input
1241 value={newDedicatedVPS.ipAddress}
1242 onChange={(e) =>
1243 setNewDedicatedVPS({
1244 ...newDedicatedVPS,
1245 ipAddress: e.target.value,
1246 })
1247 }
1248 placeholder="192.168.1.102"
1249 />
1250 </div>
1251 <div>
1252 <Label>Domain (Optional)</Label>
1253 <Input
1254 value={newDedicatedVPS.domain}
1255 onChange={(e) =>
1256 setNewDedicatedVPS({
1257 ...newDedicatedVPS,
1258 domain: e.target.value,
1259 })
1260 }
1261 placeholder="enterprise-2.com"
1262 />
1263 </div>
1264 </div>
1265 <Button
1266 onClick={createDedicatedVPS}
1267 className="w-full"
1268 >
1269 Create Dedicated VPS
1270 </Button>
1271 </div>
1272 </DialogContent>
1273 </Dialog>
1274 </div>
1275
1276 {parent.dedicatedVPS.length > 0 ? (
1277 <div className="grid gap-2">
1278 {parent.dedicatedVPS.map((dedicated) => (
1279 <div
1280 key={dedicated.id}
1281 className="flex items-center justify-between p-3 bg-accent/50 rounded-lg"
1282 >
1283 <div className="flex items-center space-x-3">
1284 <div
1285 className={`w-2 h-2 rounded-full ${getStatusColor(
1286 dedicated.status
1287 )}`}
1288 />
1289 <div>
1290 <p className="font-medium">
1291 {dedicated.name}
1292 </p>
1293 <p className="text-xs text-muted-foreground">
1294 {dedicated.customerName} •{" "}
1295 {dedicated.cpu}C/{dedicated.ram}GB/
1296 {dedicated.storage}GB •{" "}
1297 {dedicated.ipAddress}
1298 </p>
1299 </div>
1300 </div>
1301 <div className="flex items-center space-x-2">
1302 <Badge variant="outline">
1303 {dedicated.status}
1304 </Badge>
1305 <Button variant="ghost" size="sm">
1306 <Settings className="h-3 w-3" />
1307 </Button>
1308 </div>
1309 </div>
1310 ))}
1311 </div>
1312 ) : (
1313 <p className="text-sm text-muted-foreground text-center py-4">
1314 No dedicated VPS instances
1315 </p>
1316 )}
1317 </div>
1318 </motion.div>
1319 )}
1320 </AnimatePresence>
1321 </CardContent>
1322 </Card>
1323 ))}
1324 </div>
1325 </TabsContent>
1326
1327 {/* Shared VPS Tab */}
1328 <TabsContent value="shared" className="space-y-6">
1329 <div className="flex justify-between items-center">
1330 <h2 className="text-xl font-semibold">Shared VPS Instances</h2>
1331 <Button>
1332 <Plus className="h-4 w-4 mr-2" />
1333 Add Shared VPS
1334 </Button>
1335 </div>
1336
1337 <div className="grid gap-4">
1338 {vpsParents.flatMap((parent) =>
1339 parent.sharedVPS.map((shared) => (
1340 <Card key={shared.id}>
1341 <CardHeader>
1342 <div className="flex items-center justify-between">
1343 <div>
1344 <CardTitle className="flex items-center space-x-2">
1345 <span>{shared.name}</span>
1346 <Badge
1347 variant={
1348 shared.status === "running"
1349 ? "default"
1350 : "destructive"
1351 }
1352 >
1353 {shared.status}
1354 </Badge>
1355 </CardTitle>
1356 <CardDescription>
1357 Customer: {shared.customerName} • Parent:{" "}
1358 {parent.name}
1359 </CardDescription>
1360 </div>
1361 <div className="flex items-center space-x-2">
1362 <Button variant="outline" size="sm">
1363 <Play className="h-4 w-4" />
1364 </Button>
1365 <Button variant="outline" size="sm">
1366 <Square className="h-4 w-4" />
1367 </Button>
1368 <Button variant="outline" size="sm">
1369 <RotateCcw className="h-4 w-4" />
1370 </Button>
1371 <Button variant="outline" size="sm">
1372 <Settings className="h-4 w-4" />
1373 </Button>
1374 </div>
1375 </div>
1376 </CardHeader>
1377 <CardContent>
1378 <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
1379 <div className="flex items-center space-x-2">
1380 <Cpu className="h-4 w-4 text-muted-foreground" />
1381 <span className="text-sm">
1382 {shared.allocatedCpu} cores
1383 </span>
1384 </div>
1385 <div className="flex items-center space-x-2">
1386 <MemoryStick className="h-4 w-4 text-muted-foreground" />
1387 <span className="text-sm">
1388 {shared.allocatedRam} GB RAM
1389 </span>
1390 </div>
1391 <div className="flex items-center space-x-2">
1392 <HardDrive className="h-4 w-4 text-muted-foreground" />
1393 <span className="text-sm">
1394 {shared.allocatedStorage} GB Storage
1395 </span>
1396 </div>
1397 <div className="flex items-center space-x-2">
1398 <Globe className="h-4 w-4 text-muted-foreground" />
1399 <span className="text-sm">
1400 {shared.domain || "No domain"}
1401 </span>
1402 </div>
1403 </div>
1404 </CardContent>
1405 </Card>
1406 ))
1407 )}
1408 </div>
1409 </TabsContent>
1410
1411 {/* Dedicated VPS Tab */}
1412 <TabsContent value="dedicated" className="space-y-6">
1413 <div className="flex justify-between items-center">
1414 <h2 className="text-xl font-semibold">
1415 Dedicated VPS Instances
1416 </h2>
1417 <Button>
1418 <Plus className="h-4 w-4 mr-2" />
1419 Add Dedicated VPS
1420 </Button>
1421 </div>
1422
1423 <div className="grid gap-4">
1424 {vpsParents.flatMap((parent) =>
1425 parent.dedicatedVPS.map((dedicated) => (
1426 <Card key={dedicated.id}>
1427 <CardHeader>
1428 <div className="flex items-center justify-between">
1429 <div>
1430 <CardTitle className="flex items-center space-x-2">
1431 <span>{dedicated.name}</span>
1432 <Badge
1433 variant={
1434 dedicated.status === "running"
1435 ? "default"
1436 : "destructive"
1437 }
1438 >
1439 {dedicated.status}
1440 </Badge>
1441 </CardTitle>
1442 <CardDescription>
1443 Customer: {dedicated.customerName} • Parent:{" "}
1444 {parent.name}
1445 </CardDescription>
1446 </div>
1447 <div className="flex items-center space-x-2">
1448 <Button variant="outline" size="sm">
1449 <Play className="h-4 w-4" />
1450 </Button>
1451 <Button variant="outline" size="sm">
1452 <Square className="h-4 w-4" />
1453 </Button>
1454 <Button variant="outline" size="sm">
1455 <RotateCcw className="h-4 w-4" />
1456 </Button>
1457 <Button variant="outline" size="sm">
1458 <Settings className="h-4 w-4" />
1459 </Button>
1460 </div>
1461 </div>
1462 </CardHeader>
1463 <CardContent>
1464 <div className="grid grid-cols-1 md:grid-cols-5 gap-4">
1465 <div className="flex items-center space-x-2">
1466 <Cpu className="h-4 w-4 text-muted-foreground" />
1467 <span className="text-sm">
1468 {dedicated.cpu} cores
1469 </span>
1470 </div>
1471 <div className="flex items-center space-x-2">
1472 <MemoryStick className="h-4 w-4 text-muted-foreground" />
1473 <span className="text-sm">
1474 {dedicated.ram} GB RAM
1475 </span>
1476 </div>
1477 <div className="flex items-center space-x-2">
1478 <HardDrive className="h-4 w-4 text-muted-foreground" />
1479 <span className="text-sm">
1480 {dedicated.storage} GB Storage
1481 </span>
1482 </div>
1483 <div className="flex items-center space-x-2">
1484 <Server className="h-4 w-4 text-muted-foreground" />
1485 <span className="text-sm">
1486 {dedicated.ipAddress}
1487 </span>
1488 </div>
1489 <div className="flex items-center space-x-2">
1490 <Globe className="h-4 w-4 text-muted-foreground" />
1491 <span className="text-sm">
1492 {dedicated.domain || "No domain"}
1493 </span>
1494 </div>
1495 </div>
1496 </CardContent>
1497 </Card>
1498 ))
1499 )}
1500 </div>
1501 </TabsContent>
1502
1503 {/* Monitoring Tab */}
1504 <TabsContent value="monitoring" className="space-y-6">
1505 <h2 className="text-xl font-semibold">System Monitoring</h2>
1506
1507 <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
1508 <Card>
1509 <CardHeader>
1510 <CardTitle>Resource Usage Overview</CardTitle>
1511 </CardHeader>
1512 <CardContent>
1513 <div className="space-y-4">
1514 {vpsParents.map((parent) => (
1515 <div key={parent.id} className="space-y-2">
1516 <div className="flex items-center justify-between">
1517 <span className="font-medium">{parent.name}</span>
1518 <Badge
1519 variant={
1520 parent.status === "online"
1521 ? "default"
1522 : "destructive"
1523 }
1524 >
1525 {parent.status}
1526 </Badge>
1527 </div>
1528 <div className="grid grid-cols-3 gap-2 text-xs">
1529 <div>
1530 <div className="flex justify-between mb-1">
1531 <span>CPU</span>
1532 <span>
1533 {Math.round(
1534 (parent.usedResources.cpu / parent.cpu) *
1535 100
1536 )}
1537 %
1538 </span>
1539 </div>
1540 <Progress
1541 value={
1542 (parent.usedResources.cpu / parent.cpu) * 100
1543 }
1544 className="h-2"
1545 />
1546 </div>
1547 <div>
1548 <div className="flex justify-between mb-1">
1549 <span>RAM</span>
1550 <span>
1551 {Math.round(
1552 (parent.usedResources.ram / parent.ram) *
1553 100
1554 )}
1555 %
1556 </span>
1557 </div>
1558 <Progress
1559 value={
1560 (parent.usedResources.ram / parent.ram) * 100
1561 }
1562 className="h-2"
1563 />
1564 </div>
1565 <div>
1566 <div className="flex justify-between mb-1">
1567 <span>Storage</span>
1568 <span>
1569 {Math.round(
1570 (parent.usedResources.storage /
1571 parent.storage) *
1572 100
1573 )}
1574 %
1575 </span>
1576 </div>
1577 <Progress
1578 value={
1579 (parent.usedResources.storage /
1580 parent.storage) *
1581 100
1582 }
1583 className="h-2"
1584 />
1585 </div>
1586 </div>
1587 </div>
1588 ))}
1589 </div>
1590 </CardContent>
1591 </Card>
1592
1593 <Card>
1594 <CardHeader>
1595 <CardTitle>System Alerts</CardTitle>
1596 </CardHeader>
1597 <CardContent>
1598 <div className="space-y-3">
1599 {[
1600 {
1601 type: "warning",
1602 message: "High CPU usage on US-East-Primary",
1603 time: "5 min ago",
1604 },
1605 {
1606 type: "info",
1607 message: "Maintenance scheduled for EU-West-Primary",
1608 time: "1 hour ago",
1609 },
1610 {
1611 type: "success",
1612 message: "Backup completed successfully",
1613 time: "2 hours ago",
1614 },
1615 {
1616 type: "error",
1617 message: "Failed to start VPS instance",
1618 time: "3 hours ago",
1619 },
1620 ].map((alert, index) => (
1621 <div
1622 key={index}
1623 className="flex items-start space-x-3 p-3 rounded-lg bg-accent/50"
1624 >
1625 <div
1626 className={`w-2 h-2 rounded-full mt-2 ${
1627 alert.type === "error"
1628 ? "bg-red-500"
1629 : alert.type === "warning"
1630 ? "bg-yellow-500"
1631 : alert.type === "success"
1632 ? "bg-green-500"
1633 : "bg-blue-500"
1634 }`}
1635 />
1636 <div className="flex-1">
1637 <p className="text-sm font-medium">
1638 {alert.message}
1639 </p>
1640 <p className="text-xs text-muted-foreground">
1641 {alert.time}
1642 </p>
1643 </div>
1644 </div>
1645 ))}
1646 </div>
1647 </CardContent>
1648 </Card>
1649 </div>
1650 </TabsContent>
1651
1652 {/* Settings Tab */}
1653 <TabsContent value="settings" className="space-y-6">
1654 <h2 className="text-xl font-semibold">System Settings</h2>
1655
1656 <div className="grid gap-6">
1657 <Card>
1658 <CardHeader>
1659 <CardTitle>General Settings</CardTitle>
1660 <CardDescription>
1661 Configure general system preferences
1662 </CardDescription>
1663 </CardHeader>
1664 <CardContent className="space-y-4">
1665 <div className="flex items-center justify-between">
1666 <div>
1667 <Label htmlFor="auto-backup">Automatic Backups</Label>
1668 <p className="text-sm text-muted-foreground">
1669 Enable automatic daily backups
1670 </p>
1671 </div>
1672 <Switch id="auto-backup" defaultChecked />
1673 </div>
1674 <div className="flex items-center justify-between">
1675 <div>
1676 <Label htmlFor="monitoring">Real-time Monitoring</Label>
1677 <p className="text-sm text-muted-foreground">
1678 Enable real-time resource monitoring
1679 </p>
1680 </div>
1681 <Switch id="monitoring" defaultChecked />
1682 </div>
1683 <div className="flex items-center justify-between">
1684 <div>
1685 <Label htmlFor="notifications">
1686 Email Notifications
1687 </Label>
1688 <p className="text-sm text-muted-foreground">
1689 Receive email alerts for system events
1690 </p>
1691 </div>
1692 <Switch id="notifications" defaultChecked />
1693 </div>
1694 </CardContent>
1695 </Card>
1696
1697 <Card>
1698 <CardHeader>
1699 <CardTitle>Resource Limits</CardTitle>
1700 <CardDescription>
1701 Set default resource allocation limits
1702 </CardDescription>
1703 </CardHeader>
1704 <CardContent className="space-y-4">
1705 <div className="grid grid-cols-3 gap-4">
1706 <div>
1707 <Label htmlFor="max-cpu">Max CPU per Shared VPS</Label>
1708 <Input id="max-cpu" type="number" defaultValue="8" />
1709 </div>
1710 <div>
1711 <Label htmlFor="max-ram">
1712 Max RAM per Shared VPS (GB)
1713 </Label>
1714 <Input id="max-ram" type="number" defaultValue="16" />
1715 </div>
1716 <div>
1717 <Label htmlFor="max-storage">
1718 Max Storage per Shared VPS (GB)
1719 </Label>
1720 <Input
1721 id="max-storage"
1722 type="number"
1723 defaultValue="200"
1724 />
1725 </div>
1726 </div>
1727 </CardContent>
1728 </Card>
1729 </div>
1730 </TabsContent>
1731 </Tabs>
1732 </main>
1733 </div>
1734 </div>
1735 );
1736};
1737
1738export default VPSAdminDashboard;
Dependencies
External Libraries
framer-motionlucide-reactreact
Shadcn/UI Components
badgebuttoncarddialoginputlabelprogressselectseparatorswitchtabstextarea