1
0
mirror of https://github.com/community-scripts/ProxmoxVE.git synced 2025-04-29 09:23:08 +00:00

JSON editor note fix (#3260)

This commit is contained in:
Bas van den Berg 2025-03-19 12:04:58 +01:00 committed by GitHub
parent 6072e72974
commit 6fa198147b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,11 +1,11 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { import {
Select, Select,
SelectContent, SelectContent,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { AlertColors } from "@/config/siteConfig"; import { AlertColors } from "@/config/siteConfig";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -15,116 +15,136 @@ import { ScriptSchema, type Script } from "../_schemas/schemas";
import { memo, useCallback, useRef } from "react"; import { memo, useCallback, useRef } from "react";
type NoteProps = { type NoteProps = {
script: Script; script: Script;
setScript: (script: Script) => void; setScript: (script: Script) => void;
setIsValid: (isValid: boolean) => void; setIsValid: (isValid: boolean) => void;
setZodErrors: (zodErrors: z.ZodError | null) => void; setZodErrors: (zodErrors: z.ZodError | null) => void;
}; };
function Note({ function Note({
script, script,
setScript, setScript,
setIsValid, setIsValid,
setZodErrors, setZodErrors,
}: NoteProps) { }: NoteProps) {
const inputRefs = useRef<(HTMLInputElement | null)[]>([]); const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
const addNote = useCallback(() => { const addNote = useCallback(() => {
setScript({ setScript({
...script, ...script,
notes: [...script.notes, { text: "", type: "" }], notes: [...script.notes, { text: "", type: "" }],
}); });
}, [script, setScript]); }, [script, setScript]);
const updateNote = useCallback(( const updateNote = useCallback((
index: number, index: number,
key: keyof Script["notes"][number], key: keyof Script["notes"][number],
value: string, value: string,
) => { ) => {
const updated: Script = { const updated: Script = {
...script, ...script,
notes: script.notes.map((note, i) => notes: script.notes.map((note, i) =>
i === index ? { ...note, [key]: value } : note, i === index ? { ...note, [key]: value } : note,
), ),
}; };
const result = ScriptSchema.safeParse(updated); const result = ScriptSchema.safeParse(updated);
setIsValid(result.success); setIsValid(result.success);
setZodErrors(result.success ? null : result.error); setZodErrors(result.success ? null : result.error);
setScript(updated); setScript(updated);
// Restore focus after state update // Restore focus after state update
if (key === "text") { if (key === "text") {
setTimeout(() => { setTimeout(() => {
inputRefs.current[index]?.focus(); inputRefs.current[index]?.focus();
}, 0); }, 0);
} }
}, [script, setScript, setIsValid, setZodErrors]); }, [script, setScript, setIsValid, setZodErrors]);
const removeNote = useCallback((index: number) => { const removeNote = useCallback((index: number) => {
setScript({ setScript({
...script, ...script,
notes: script.notes.filter((_, i) => i !== index), notes: script.notes.filter((_, i) => i !== index),
}); });
}, [script, setScript]); }, [script, setScript]);
const NoteItem = memo( return (
({ note, index }: { note: Script["notes"][number]; index: number }) => ( <>
<div className="space-y-2 border p-4 rounded"> <h3 className="text-xl font-semibold">Notes</h3>
<Input {script.notes.map((note, index) => (
placeholder="Note Text" <NoteItem key={index} note={note} index={index} updateNote={updateNote} removeNote={removeNote} />
value={note.text}
onChange={(e) => updateNote(index, "text", e.target.value)}
ref={(el) => {
inputRefs.current[index] = el;
}}
/>
<Select
value={note.type}
onValueChange={(value) => updateNote(index, "type", value)}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent>
{Object.keys(AlertColors).map((type) => (
<SelectItem key={type} value={type}>
<span className="flex items-center gap-2">
{type.charAt(0).toUpperCase() + type.slice(1)}{" "}
<div
className={cn(
"size-4 rounded-full border",
AlertColors[type as keyof typeof AlertColors],
)}
/>
</span>
</SelectItem>
))} ))}
</SelectContent> <Button type="button" size="sm" onClick={addNote}>
</Select> <PlusCircle className="mr-2 h-4 w-4" /> Add Note
<Button </Button>
size="sm" </>
variant="destructive" );
type="button"
onClick={() => removeNote(index)}
>
<Trash2 className="mr-2 h-4 w-4" /> Remove Note
</Button>
</div>
),
);
NoteItem.displayName = 'NoteItem';
return (
<>
<h3 className="text-xl font-semibold">Notes</h3>
{script.notes.map((note, index) => (
<NoteItem key={index} note={note} index={index} />
))}
<Button type="button" size="sm" onClick={addNote}>
<PlusCircle className="mr-2 h-4 w-4" /> Add Note
</Button>
</>
);
} }
export default memo(Note); const NoteItem = memo(
({
note,
index,
updateNote,
removeNote,
}: {
note: Script["notes"][number];
index: number;
updateNote: (index: number, key: keyof Script["notes"][number], value: string) => void;
removeNote: (index: number) => void;
}) => {
const inputRef = useRef<HTMLInputElement | null>(null);
const handleTextChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
updateNote(index, "text", e.target.value);
setTimeout(() => {
inputRef.current?.focus();
}, 0);
}, [index, updateNote]);
return (
<div className="space-y-2 border p-4 rounded">
<Input
placeholder="Note Text"
value={note.text}
onChange={handleTextChange}
ref={inputRef}
/>
<Select
value={note.type}
onValueChange={(value) => updateNote(index, "type", value)}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent>
{Object.keys(AlertColors).map((type) => (
<SelectItem key={type} value={type}>
<span className="flex items-center gap-2">
{type.charAt(0).toUpperCase() + type.slice(1)}{" "}
<div
className={cn(
"size-4 rounded-full border",
AlertColors[type as keyof typeof AlertColors],
)}
/>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
<Button
size="sm"
variant="destructive"
type="button"
onClick={() => removeNote(index)}
>
<Trash2 className="mr-2 h-4 w-4" /> Remove Note
</Button>
</div>
);
}
);
NoteItem.displayName = 'NoteItem';
export default memo(Note);