Files
2026-06-15 16:21:53 -05:00

97 lines
3.4 KiB
TypeScript

import React from 'react'
import { Input, Textarea } from '@/components/ui'
import { FieldType } from '@/types'
export interface FormFieldDef {
id: string
label: string
type: FieldType
hint?: string | null
options: string[]
required: boolean
}
export function FieldRenderer({ field, value, onChange }: {
field: FormFieldDef
value: any
onChange: (v: any) => void
}) {
switch (field.type) {
case 'SHORT_TEXT':
return <Input value={value || ''} onChange={e => onChange(e.target.value)} placeholder={field.hint || ''}/>
case 'LONG_TEXT':
return <Textarea value={value || ''} onChange={e => onChange(e.target.value)} placeholder={field.hint || ''}/>
case 'NUMBER':
return <Input type="number" value={value ?? ''} onChange={e => onChange(e.target.value)} style={{ maxWidth: '160px' }}/>
case 'DATE':
return <Input type="date" value={value || ''} onChange={e => onChange(e.target.value)} style={{ maxWidth: '180px' }}/>
case 'SINGLE_CHOICE':
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
{field.options.map(opt => (
<label key={opt} style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '12px', cursor: 'pointer' }}>
<input type="radio" name={field.id} checked={value === opt} onChange={() => onChange(opt)} style={{ accentColor: '#534AB7' }}/>
{opt}
</label>
))}
</div>
)
case 'MULTI_CHOICE':
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
{field.options.map(opt => (
<label key={opt} style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '12px', cursor: 'pointer' }}>
<input type="checkbox" checked={(value || []).includes(opt)}
onChange={e => {
const cur = value || []
onChange(e.target.checked ? [...cur, opt] : cur.filter((x: string) => x !== opt))
}} style={{ accentColor: '#534AB7' }}/>
{opt}
</label>
))}
</div>
)
case 'RATING':
return (
<div style={{ display: 'flex', gap: '4px' }}>
{[1, 2, 3, 4, 5].map(n => (
<span key={n} onClick={() => onChange(n)} style={{ fontSize: '24px', cursor: 'pointer', color: (value || 0) >= n ? '#EF9F27' : '#ddd' }}></span>
))}
</div>
)
case 'PHOTO':
return <Input type="file" accept="image/*" onChange={e => onChange(e.target.files?.[0]?.name || '')}/>
default:
return null
}
}
export function FormFieldList({ fields, values, onChange }: {
fields: FormFieldDef[]
values: Record<string, any>
onChange: (fieldId: string, value: any) => void
}) {
return (
<>
{fields.map(field => (
<div key={field.id} style={{ marginBottom: '14px' }}>
<label style={{ display: 'block', fontSize: '12px', fontWeight: '500', marginBottom: '4px' }}>
{field.label}
{field.required && <span style={{ color: '#E24B4A', marginLeft: '2px' }}>*</span>}
</label>
{field.hint && <div style={{ fontSize: '10px', color: '#aaa', marginBottom: '4px' }}>{field.hint}</div>}
<FieldRenderer field={field} value={values[field.id]} onChange={v => onChange(field.id, v)}/>
</div>
))}
</>
)
}