mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2025-02-01 17:31:49 +00:00
Enhance InstallMethod component: add operating system selection and version handling with new input structure (#426)
This commit is contained in:
parent
0c744ad274
commit
dc1b14dfd9
@ -1,4 +1,5 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@ -6,11 +7,11 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { OperatingSystems } from "@/config/siteConfig";
|
||||||
import { PlusCircle, Trash2 } from "lucide-react";
|
import { PlusCircle, Trash2 } from "lucide-react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { memo, useCallback, useEffect, useRef } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { InstallMethodSchema, ScriptSchema } from "../_schemas/schemas";
|
import { InstallMethodSchema, ScriptSchema } from "../_schemas/schemas";
|
||||||
import { memo, useCallback } from "react";
|
|
||||||
|
|
||||||
type Script = z.infer<typeof ScriptSchema>;
|
type Script = z.infer<typeof ScriptSchema>;
|
||||||
|
|
||||||
@ -27,6 +28,10 @@ function InstallMethod({
|
|||||||
setIsValid,
|
setIsValid,
|
||||||
setZodErrors,
|
setZodErrors,
|
||||||
}: InstallMethodProps) {
|
}: InstallMethodProps) {
|
||||||
|
const cpuRefs = useRef<(HTMLInputElement | null)[]>([]);
|
||||||
|
const ramRefs = useRef<(HTMLInputElement | null)[]>([]);
|
||||||
|
const hddRefs = useRef<(HTMLInputElement | null)[]>([]);
|
||||||
|
|
||||||
const addInstallMethod = useCallback(() => {
|
const addInstallMethod = useCallback(() => {
|
||||||
setScript((prev) => {
|
setScript((prev) => {
|
||||||
const method = InstallMethodSchema.parse({
|
const method = InstallMethodSchema.parse({
|
||||||
@ -47,71 +52,62 @@ function InstallMethod({
|
|||||||
});
|
});
|
||||||
}, [setScript]);
|
}, [setScript]);
|
||||||
|
|
||||||
const updateInstallMethod = useCallback((
|
const updateInstallMethod = useCallback(
|
||||||
index: number,
|
(
|
||||||
key: keyof Script["install_methods"][number],
|
index: number,
|
||||||
value: Script["install_methods"][number][keyof Script["install_methods"][number]],
|
key: keyof Script["install_methods"][number],
|
||||||
) => {
|
value: Script["install_methods"][number][keyof Script["install_methods"][number]],
|
||||||
setScript((prev) => {
|
) => {
|
||||||
const updatedMethods = prev.install_methods.map((method, i) => {
|
setScript((prev) => {
|
||||||
if (i === index) {
|
const updatedMethods = prev.install_methods.map((method, i) => {
|
||||||
const updatedMethod = { ...method, [key]: value };
|
if (i === index) {
|
||||||
|
const updatedMethod = { ...method, [key]: value };
|
||||||
|
|
||||||
if (key === "type") {
|
if (key === "type") {
|
||||||
updatedMethod.script =
|
updatedMethod.script =
|
||||||
value === "alpine"
|
value === "alpine"
|
||||||
? `/${prev.type}/alpine-${prev.slug}.sh`
|
? `/${prev.type}/alpine-${prev.slug}.sh`
|
||||||
: `/${prev.type}/${prev.slug}.sh`;
|
: `/${prev.type}/${prev.slug}.sh`;
|
||||||
|
|
||||||
|
// Set OS to Alpine and reset version if type is alpine
|
||||||
|
if (value === "alpine") {
|
||||||
|
updatedMethod.resources.os = "Alpine";
|
||||||
|
updatedMethod.resources.version = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedMethod;
|
||||||
}
|
}
|
||||||
|
return method;
|
||||||
|
});
|
||||||
|
|
||||||
return updatedMethod;
|
const updated = {
|
||||||
|
...prev,
|
||||||
|
install_methods: updatedMethods,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = ScriptSchema.safeParse(updated);
|
||||||
|
setIsValid(result.success);
|
||||||
|
if (!result.success) {
|
||||||
|
setZodErrors(result.error);
|
||||||
|
} else {
|
||||||
|
setZodErrors(null);
|
||||||
}
|
}
|
||||||
return method;
|
return updated;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
[setScript, setIsValid, setZodErrors],
|
||||||
|
);
|
||||||
|
|
||||||
const updated = {
|
const removeInstallMethod = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
setScript((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
install_methods: updatedMethods,
|
install_methods: prev.install_methods.filter((_, i) => i !== index),
|
||||||
};
|
}));
|
||||||
|
},
|
||||||
const result = ScriptSchema.safeParse(updated);
|
[setScript],
|
||||||
setIsValid(result.success);
|
);
|
||||||
if (!result.success) {
|
|
||||||
setZodErrors(result.error);
|
|
||||||
} else {
|
|
||||||
setZodErrors(null);
|
|
||||||
}
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
}, [setScript, setIsValid, setZodErrors]);
|
|
||||||
|
|
||||||
const removeInstallMethod = useCallback((index: number) => {
|
|
||||||
setScript((prev) => ({
|
|
||||||
...prev,
|
|
||||||
install_methods: prev.install_methods.filter((_, i) => i !== index),
|
|
||||||
}));
|
|
||||||
}, [setScript]);
|
|
||||||
|
|
||||||
const ResourceInput = memo(({
|
|
||||||
placeholder,
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
type = "text"
|
|
||||||
}: {
|
|
||||||
placeholder: string;
|
|
||||||
value: string | number | null;
|
|
||||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
type?: string;
|
|
||||||
}) => (
|
|
||||||
<Input
|
|
||||||
placeholder={placeholder}
|
|
||||||
type={type}
|
|
||||||
value={value || ""}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
ResourceInput.displayName = 'ResourceInput';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -131,10 +127,13 @@ function InstallMethod({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<ResourceInput
|
<Input
|
||||||
|
ref={(el) => {
|
||||||
|
cpuRefs.current[index] = el;
|
||||||
|
}}
|
||||||
placeholder="CPU in Cores"
|
placeholder="CPU in Cores"
|
||||||
type="number"
|
type="number"
|
||||||
value={method.resources.cpu}
|
value={method.resources.cpu || ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateInstallMethod(index, "resources", {
|
updateInstallMethod(index, "resources", {
|
||||||
...method.resources,
|
...method.resources,
|
||||||
@ -142,10 +141,13 @@ function InstallMethod({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResourceInput
|
<Input
|
||||||
|
ref={(el) => {
|
||||||
|
ramRefs.current[index] = el;
|
||||||
|
}}
|
||||||
placeholder="RAM in MB"
|
placeholder="RAM in MB"
|
||||||
type="number"
|
type="number"
|
||||||
value={method.resources.ram}
|
value={method.resources.ram || ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateInstallMethod(index, "resources", {
|
updateInstallMethod(index, "resources", {
|
||||||
...method.resources,
|
...method.resources,
|
||||||
@ -153,10 +155,13 @@ function InstallMethod({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResourceInput
|
<Input
|
||||||
placeholder="HDD in GB"
|
ref={(el) => {
|
||||||
|
hddRefs.current[index] = el;
|
||||||
|
}}
|
||||||
|
placeholder="HDD in GB"
|
||||||
type="number"
|
type="number"
|
||||||
value={method.resources.hdd}
|
value={method.resources.hdd || ""}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateInstallMethod(index, "resources", {
|
updateInstallMethod(index, "resources", {
|
||||||
...method.resources,
|
...method.resources,
|
||||||
@ -166,27 +171,51 @@ function InstallMethod({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<ResourceInput
|
<Select
|
||||||
placeholder="OS"
|
value={method.resources.os || undefined}
|
||||||
value={method.resources.os}
|
onValueChange={(value) =>
|
||||||
onChange={(e) =>
|
|
||||||
updateInstallMethod(index, "resources", {
|
updateInstallMethod(index, "resources", {
|
||||||
...method.resources,
|
...method.resources,
|
||||||
os: e.target.value || null,
|
os: value || null,
|
||||||
|
version: null, // Reset version when OS changes
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
disabled={method.type === "alpine"}
|
||||||
<ResourceInput
|
>
|
||||||
placeholder="Version"
|
<SelectTrigger>
|
||||||
type="number"
|
<SelectValue placeholder="OS" />
|
||||||
value={method.resources.version}
|
</SelectTrigger>
|
||||||
onChange={(e) =>
|
<SelectContent>
|
||||||
|
{OperatingSystems.map((os) => (
|
||||||
|
<SelectItem key={os.name} value={os.name}>
|
||||||
|
{os.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<Select
|
||||||
|
value={method.resources.version ? String(method.resources.version) : undefined}
|
||||||
|
onValueChange={(value) =>
|
||||||
updateInstallMethod(index, "resources", {
|
updateInstallMethod(index, "resources", {
|
||||||
...method.resources,
|
...method.resources,
|
||||||
version: e.target.value ? Number(e.target.value) : null,
|
version: value ? Number(value) : null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
disabled={method.type === "alpine"}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Version" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{OperatingSystems.find(
|
||||||
|
(os) => os.name === method.resources.os,
|
||||||
|
)?.versions.map((version) => (
|
||||||
|
<SelectItem key={version.slug} value={version.name}>
|
||||||
|
{version.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { OperatingSystem } from "@/lib/types";
|
||||||
import { MessagesSquare, Scroll } from "lucide-react";
|
import { MessagesSquare, Scroll } from "lucide-react";
|
||||||
import { FaDiscord, FaGithub } from "react-icons/fa";
|
import { FaDiscord, FaGithub } from "react-icons/fa";
|
||||||
|
|
||||||
@ -44,4 +45,21 @@ export const analytics = {
|
|||||||
export const AlertColors = {
|
export const AlertColors = {
|
||||||
warning: "border-red-500/25 bg-destructive/25",
|
warning: "border-red-500/25 bg-destructive/25",
|
||||||
info: "border-cyan-500/25 bg-cyan-50 dark:border-cyan-900 dark:bg-cyan-900/25",
|
info: "border-cyan-500/25 bg-cyan-50 dark:border-cyan-900 dark:bg-cyan-900/25",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const OperatingSystems: OperatingSystem[] = [
|
||||||
|
{
|
||||||
|
name: "Debian",
|
||||||
|
versions: [
|
||||||
|
{ name: "11", slug: "bullseye" },
|
||||||
|
{ name: "12", slug: "bookworm" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Ubuntu",
|
||||||
|
versions: [
|
||||||
|
{ name: "22.04", slug: "jammy" },
|
||||||
|
{ name: "24.04", slug: "noble" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -46,3 +46,13 @@ export type Category = {
|
|||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface Version {
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OperatingSystem {
|
||||||
|
name: string;
|
||||||
|
versions: Version[];
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user