Files
prowler/ui/components/providers/forms/edit-name-form.tsx
2026-07-08 16:57:02 +02:00

124 lines
3.3 KiB
TypeScript

"use client";
import type { Dispatch, FormEvent, SetStateAction } from "react";
import { useState } from "react";
import { SaveIcon } from "@/components/icons";
import { Button } from "@/components/shadcn";
import { useToast } from "@/components/shadcn";
import { Input } from "@/components/shadcn/input/input";
interface EditNameFormProps {
currentValue: string;
label: string;
successMessage: string;
placeholder?: string;
helperText?: string;
validate?: (value: string) => string | null;
setIsOpen: Dispatch<SetStateAction<boolean>>;
onSave: (value: string) => Promise<unknown>;
}
export function EditNameForm({
currentValue,
label,
successMessage,
placeholder,
helperText,
validate,
setIsOpen,
onSave,
}: EditNameFormProps) {
const [value, setValue] = useState(currentValue);
const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const { toast } = useToast();
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const trimmed = value.trim();
if (validate) {
const validationError = validate(trimmed);
if (validationError) {
setError(validationError);
return;
}
}
setError(null);
setIsLoading(true);
const result = (await onSave(trimmed)) as Record<string, unknown>;
setIsLoading(false);
const errors = result?.errors as Array<{ detail: string }> | undefined;
if (errors && errors.length > 0) {
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: `${errors[0].detail}`,
});
} else if (result?.error) {
toast({
variant: "destructive",
title: "Oops! Something went wrong",
description: String(result.error),
});
} else {
toast({ title: "Success!", description: successMessage });
setIsOpen(false);
}
};
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<div className="text-md">
Current {label.toLowerCase()}:{" "}
<span className="font-bold">{currentValue || "—"}</span>
</div>
<div className="flex flex-col gap-2">
<label
htmlFor="edit-name-input"
className="text-text-neutral-primary text-sm font-medium"
>
{label}
</label>
<Input
id="edit-name-input"
value={value}
onChange={(e) => {
setValue(e.target.value);
if (error) setError(null);
}}
placeholder={placeholder ?? currentValue}
disabled={isLoading}
aria-invalid={!!error}
/>
{error && <p className="text-destructive text-xs">{error}</p>}
{helperText && !error && (
<p className="text-muted-foreground text-xs">{helperText}</p>
)}
</div>
<div className="flex w-full justify-end gap-4">
<Button
type="button"
variant="ghost"
size="lg"
onClick={() => setIsOpen(false)}
disabled={isLoading}
>
Cancel
</Button>
<Button type="submit" size="lg" disabled={isLoading}>
{!isLoading && <SaveIcon size={24} />}
{isLoading ? "Loading" : "Save"}
</Button>
</div>
</form>
);
}