ldc-ovoteam/resources/ts/back/Components/Inputs/SimpleString.tsx
Jérémy Munsch 47cd01141d
feat(validation): enhance input validation limits and prepare data for requests
- Added data preparation for validation in StoreProductRequest and StoreRecipeRequest.
- Updated validation rules to include minimum lengths for various fields.
- Integrated limits for word counting in SimpleString, SimpleTextarea, and SimpleWysiwyg components.
2026-10-22 14:35:05 +02:00

114 lines
3.2 KiB
TypeScript

import { usePage } from "@inertiajs/react";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import { useEffect, useRef, useState, type ReactNode } from "react";
import InputError from "@/back/Components/InputError";
import WordCounter from "@/back/Components/WordCounter";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { type MinMax } from "@/hooks/use-laravel-validation";
import trans from "@/hooks/use-translations";
import { cn } from "@/lib/utils";
interface SimpleStringProps {
className?: string;
name: string;
label: ReactNode | string;
value?: string;
id?: string;
type?: string;
placeholder?: string;
wordCounter?: boolean;
limits?: MinMax;
required?: boolean;
disabled?: boolean;
/** Pattern is regex without antislash */
pattern?: string;
checkVisibility?: boolean;
parentErrors?: Record<string, string> | null;
onBlur?: () => void;
setData: (name: string, value: string) => void;
}
export default function SimpleString({
className,
value = "",
name,
label,
id = name,
type = "text",
placeholder = "uwil-backend::back.enter_text_here",
wordCounter = true,
limits,
required = true,
disabled = false,
pattern,
checkVisibility = false,
parentErrors = null,
onBlur,
setData,
}: Readonly<SimpleStringProps>) {
const [isVisible, setIsVisible] = useState(checkVisibility);
const inputRef = useRef<HTMLInputElement>(null);
const { errors } = usePage().props;
const localErrors = { ...errors, ...parentErrors };
useEffect(() => {
if (inputRef.current && inputRef.current.value !== value) {
inputRef.current.value = value;
}
}, [value]);
return (
<div
className={cn(
"flex flex-col w-full justify-end items-start gap-1.5",
className,
)}
>
<Label htmlFor={id} className="font-bold flex items-center gap-1.5">
{label}
{required && " *"}
</Label>
{wordCounter && <WordCounter value={value} limits={limits} />}
<div className="flex w-full">
<Input
id={id}
name={name}
type={isVisible ? "text" : type}
placeholder={trans(placeholder)}
defaultValue={value}
ref={inputRef}
onChange={(e) => {
setData(name, e.target.value);
}}
required={required}
disabled={disabled}
className={cn(
type === "password" || checkVisibility
? "w-[calc(100%-40px-0.25rem)]"
: "w-full",
{ "border-destructive/50": localErrors[name] },
)}
pattern={pattern ?? undefined}
onBlur={onBlur}
/>
{(type === "password" || checkVisibility) && (
<Button
className="ms-1"
type="button"
size="icon"
onClick={() => {
setIsVisible(!isVisible);
}}
>
<EyeOffIcon className={!isVisible ? "hidden " : ""} />
<EyeIcon className={isVisible ? "hidden" : ""} />
</Button>
)}
</div>
<InputError message={localErrors[name]} />
</div>
);
}