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 { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
@ -6,11 +7,11 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { OperatingSystems } from "@/config/siteConfig";
import { PlusCircle, Trash2 } from "lucide-react";
import { Input } from "@/components/ui/input";
import { memo, useCallback, useEffect, useRef } from "react";
import { z } from "zod";
import { InstallMethodSchema, ScriptSchema } from "../_schemas/schemas";
import { memo, useCallback } from "react";
type Script = z.infer<typeof ScriptSchema>;
@ -27,6 +28,10 @@ function InstallMethod({
setIsValid,
setZodErrors,
}: InstallMethodProps) {
const cpuRefs = useRef<(HTMLInputElement | null)[]>([]);
const ramRefs = useRef<(HTMLInputElement | null)[]>([]);
const hddRefs = useRef<(HTMLInputElement | null)[]>([]);
const addInstallMethod = useCallback(() => {
setScript((prev) => {
const method = InstallMethodSchema.parse({
@ -47,71 +52,62 @@ function InstallMethod({
});
}, [setScript]);
const updateInstallMethod = useCallback((
index: 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) => {
if (i === index) {
const updatedMethod = { ...method, [key]: value };
const updateInstallMethod = useCallback(
(
index: 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) => {
if (i === index) {
const updatedMethod = { ...method, [key]: value };
if (key === "type") {
updatedMethod.script =
value === "alpine"
? `/${prev.type}/alpine-${prev.slug}.sh`
: `/${prev.type}/${prev.slug}.sh`;
if (key === "type") {
updatedMethod.script =
value === "alpine"
? `/${prev.type}/alpine-${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,
install_methods: updatedMethods,
};
const result = ScriptSchema.safeParse(updated);
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';
install_methods: prev.install_methods.filter((_, i) => i !== index),
}));
},
[setScript],
);
return (
<>
@ -131,10 +127,13 @@ function InstallMethod({
</SelectContent>
</Select>
<div className="flex gap-2">
<ResourceInput
<Input
ref={(el) => {
cpuRefs.current[index] = el;
}}
placeholder="CPU in Cores"
type="number"
value={method.resources.cpu}
value={method.resources.cpu || ""}
onChange={(e) =>
updateInstallMethod(index, "resources", {
...method.resources,
@ -142,10 +141,13 @@ function InstallMethod({
})
}
/>
<ResourceInput
<Input
ref={(el) => {
ramRefs.current[index] = el;
}}
placeholder="RAM in MB"
type="number"
value={method.resources.ram}
value={method.resources.ram || ""}
onChange={(e) =>
updateInstallMethod(index, "resources", {
...method.resources,
@ -153,10 +155,13 @@ function InstallMethod({
})
}
/>
<ResourceInput
placeholder="HDD in GB"
<Input
ref={(el) => {
hddRefs.current[index] = el;
}}
placeholder="HDD in GB"
type="number"
value={method.resources.hdd}
value={method.resources.hdd || ""}
onChange={(e) =>
updateInstallMethod(index, "resources", {
...method.resources,
@ -166,27 +171,51 @@ function InstallMethod({
/>
</div>
<div className="flex gap-2">
<ResourceInput
placeholder="OS"
value={method.resources.os}
onChange={(e) =>
<Select
value={method.resources.os || undefined}
onValueChange={(value) =>
updateInstallMethod(index, "resources", {
...method.resources,
os: e.target.value || null,
os: value || null,
version: null, // Reset version when OS changes
})
}
/>
<ResourceInput
placeholder="Version"
type="number"
value={method.resources.version}
onChange={(e) =>
disabled={method.type === "alpine"}
>
<SelectTrigger>
<SelectValue placeholder="OS" />
</SelectTrigger>
<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", {
...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>
<Button
variant="destructive"

View File

@ -1,3 +1,4 @@
import { OperatingSystem } from "@/lib/types";
import { MessagesSquare, Scroll } from "lucide-react";
import { FaDiscord, FaGithub } from "react-icons/fa";
@ -44,4 +45,21 @@ export const analytics = {
export const AlertColors = {
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",
};
};
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 = {
categories: Category[];
};
export interface Version {
name: string;
slug: string;
}
export interface OperatingSystem {
name: string;
versions: Version[];
}