1
0
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:
Bram Suurd 2024-11-23 08:14:22 +01:00 committed by GitHub
parent 0c744ad274
commit dc1b14dfd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 138 additions and 81 deletions

View File

@ -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"

View File

@ -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" },
],
},
];

View File

@ -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[];
}