kontenhumas-fe/components/form/common/FormField.tsx

192 lines
4.7 KiB
TypeScript

"use client";
import React from "react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
interface FormFieldProps {
label: string;
name: string;
type?:
| "text"
| "email"
| "password"
| "number"
| "tel"
| "url"
| "textarea"
| "select"
| "checkbox";
placeholder?: string;
value: any;
onChange: (value: any) => void;
error?: string;
required?: boolean;
disabled?: boolean;
options?: Array<{ value: string | number; label: string }>;
helpText?: string;
className?: string;
min?: number;
max?: number;
step?: number;
rows?: number;
}
export const FormField: React.FC<FormFieldProps> = ({
label,
name,
type = "text",
placeholder,
value,
onChange,
error,
required = false,
disabled = false,
options = [],
helpText,
className = "",
min,
max,
step,
rows = 3,
}) => {
const fieldId = `field-${name}`;
const hasError = !!error;
const renderField = () => {
switch (type) {
case "textarea":
return (
<Textarea
id={fieldId}
placeholder={placeholder}
value={value || ""}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
className={`${hasError ? "border-red-500" : ""} ${className}`}
rows={rows}
/>
);
case "select":
return (
<Select
value={value?.toString() || ""}
onValueChange={(val) => onChange(val)}
disabled={disabled}
>
<SelectTrigger
className={`${hasError ? "border-red-500" : ""} ${className}`}
>
<SelectValue placeholder={placeholder || `Select ${label}`} />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
<SelectItem key={option.value} value={option.value.toString()}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
);
case "checkbox":
return (
<div className="flex items-center gap-3">
<Checkbox
checked={value}
onCheckedChange={(v) => onChange(!!v)}
className="
h-4 w-4
rounded-full
border border-black
text-white
data-[state=checked]:bg-black
focus-visible:ring-2 focus-visible:ring-black
"
/>
<Label
htmlFor={fieldId}
className="text-sm font-medium leading-none cursor-pointer"
>
{label}
</Label>
</div>
);
// case "checkbox":
// return (
// <div className="flex items-center space-x-2">
// <Checkbox
// id={fieldId}
// checked={!!value}
// onCheckedChange={(checked) => onChange(checked)}
// disabled={disabled}
// className={hasError ? "border-red-500" : ""}
// />
// <Label htmlFor={fieldId} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
// {label}
// </Label>
// </div>
// );
case "number":
return (
<Input
id={fieldId}
type="number"
placeholder={placeholder}
value={value || ""}
onChange={(e) =>
onChange(e.target.value ? Number(e.target.value) : "")
}
disabled={disabled}
className={`${hasError ? "border-red-500" : ""} ${className}`}
min={min}
max={max}
step={step}
/>
);
default:
return (
<Input
id={fieldId}
type={type}
placeholder={placeholder}
value={value || ""}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
className={`${hasError ? "border-red-500" : ""} ${className}`}
/>
);
}
};
return (
<div className="space-y-2">
{type !== "checkbox" && (
<Label htmlFor={fieldId} className="text-sm font-medium">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</Label>
)}
{renderField()}
{helpText && <p className="text-xs text-gray-500">{helpText}</p>}
{hasError && <p className="text-red-500 text-xs">{error}</p>}
</div>
);
};