Skip to content

Commit 07cf03a

Browse files
Merge pull request #360 from community-scripts/feat/clone_lxc_vm
feat: Add VM/LXC cloning functionality
2 parents f3d14c6 + dd17d2c commit 07cf03a

File tree

10 files changed

+1462
-50
lines changed

10 files changed

+1462
-50
lines changed

server.js

Lines changed: 424 additions & 2 deletions
Large diffs are not rendered by default.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'use client';
2+
3+
import { useState, useEffect } from 'react';
4+
import { Button } from './ui/button';
5+
import { Input } from './ui/input';
6+
import { Copy, X } from 'lucide-react';
7+
import { useRegisterModal } from './modal/ModalStackProvider';
8+
9+
interface CloneCountInputModalProps {
10+
isOpen: boolean;
11+
onClose: () => void;
12+
onSubmit: (count: number) => void;
13+
storageName: string;
14+
}
15+
16+
export function CloneCountInputModal({
17+
isOpen,
18+
onClose,
19+
onSubmit,
20+
storageName
21+
}: CloneCountInputModalProps) {
22+
const [cloneCount, setCloneCount] = useState<number>(1);
23+
24+
useRegisterModal(isOpen, { id: 'clone-count-input-modal', allowEscape: true, onClose });
25+
26+
useEffect(() => {
27+
if (isOpen) {
28+
setCloneCount(1); // Reset to default when modal opens
29+
}
30+
}, [isOpen]);
31+
32+
if (!isOpen) return null;
33+
34+
const handleSubmit = () => {
35+
if (cloneCount >= 1) {
36+
onSubmit(cloneCount);
37+
setCloneCount(1); // Reset after submit
38+
}
39+
};
40+
41+
const handleClose = () => {
42+
setCloneCount(1); // Reset on close
43+
onClose();
44+
};
45+
46+
return (
47+
<div className="fixed inset-0 backdrop-blur-sm bg-black/50 flex items-center justify-center z-50 p-4">
48+
<div className="bg-card rounded-lg shadow-xl max-w-md w-full border border-border">
49+
{/* Header */}
50+
<div className="flex items-center justify-between p-6 border-b border-border">
51+
<div className="flex items-center gap-3">
52+
<Copy className="h-6 w-6 text-primary" />
53+
<h2 className="text-2xl font-bold text-card-foreground">Clone Count</h2>
54+
</div>
55+
<Button
56+
onClick={handleClose}
57+
variant="ghost"
58+
size="icon"
59+
className="text-muted-foreground hover:text-foreground"
60+
>
61+
<X className="h-5 w-5" />
62+
</Button>
63+
</div>
64+
65+
{/* Content */}
66+
<div className="p-6">
67+
<p className="text-sm text-muted-foreground mb-4">
68+
How many clones would you like to create?
69+
</p>
70+
71+
{storageName && (
72+
<div className="mb-4 p-3 bg-muted/50 rounded-lg">
73+
<p className="text-sm text-muted-foreground">Storage:</p>
74+
<p className="text-sm font-medium text-foreground">{storageName}</p>
75+
</div>
76+
)}
77+
78+
<div className="space-y-2 mb-6">
79+
<label htmlFor="cloneCount" className="block text-sm font-medium text-foreground">
80+
Number of Clones
81+
</label>
82+
<Input
83+
id="cloneCount"
84+
type="number"
85+
min="1"
86+
max="100"
87+
value={cloneCount}
88+
onChange={(e) => {
89+
const value = parseInt(e.target.value, 10);
90+
if (!isNaN(value) && value >= 1 && value <= 100) {
91+
setCloneCount(value);
92+
} else if (e.target.value === '') {
93+
setCloneCount(1);
94+
}
95+
}}
96+
className="w-full"
97+
placeholder="1"
98+
/>
99+
<p className="text-xs text-muted-foreground">
100+
Enter a number between 1 and 100
101+
</p>
102+
</div>
103+
104+
{/* Action Buttons */}
105+
<div className="flex flex-col sm:flex-row justify-end gap-3">
106+
<Button
107+
onClick={handleClose}
108+
variant="outline"
109+
size="default"
110+
className="w-full sm:w-auto"
111+
>
112+
Cancel
113+
</Button>
114+
<Button
115+
onClick={handleSubmit}
116+
disabled={cloneCount < 1 || cloneCount > 100}
117+
variant="default"
118+
size="default"
119+
className="w-full sm:w-auto"
120+
>
121+
Continue
122+
</Button>
123+
</div>
124+
</div>
125+
</div>
126+
</div>
127+
);
128+
}
129+

0 commit comments

Comments
 (0)