turash/bugulma/frontend/components/form/FormField.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

75 lines
1.9 KiB
TypeScript

import React from 'react';
import { Control, Controller, FieldErrors, FieldValues, Path } from 'react-hook-form';
import { spacing } from '@/lib/spacing';
interface FormFieldProps<T extends FieldValues> {
control: Control<T>;
errors: FieldErrors<T>;
name: Path<T>;
label: string;
description?: string | React.ReactNode;
required?: boolean;
component: React.ElementType;
[key: string]: unknown;
}
const get = (obj: Record<string, unknown>, path: string) => {
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
};
const FormField = <T extends FieldValues>({
control,
errors,
name,
label,
description,
required,
component: Component,
...props
}: FormFieldProps<T>) => {
const error = get(errors, name);
return (
<div className={spacing.formField}>
<label htmlFor={name} className="text-sm font-medium">
{label}
{required && <span className="text-destructive">*</span>}
</label>
{description && (
<p id={`${name}-description`} className="text-xs text-muted-foreground">
{description}
</p>
)}
<Controller
name={name}
control={control}
render={({ field }) => {
const describedBy = [
description ? `${name}-description` : undefined,
error ? `${name}-error` : undefined,
]
.filter(Boolean)
.join(' ');
return (
<Component
{...field}
{...props}
id={name}
aria-invalid={!!error}
aria-describedby={describedBy.length > 0 ? describedBy : undefined}
/>
);
}}
/>
{error && (
<p id={`${name}-error`} className="text-destructive text-xs mt-1">
{String(error.message)}
</p>
)}
</div>
);
};
export default FormField;