@@ -128,18 +128,57 @@ export const ObjectForm: React.FC<ObjectFormProps> = ({
128128 } ;
129129
130130 // Add field-specific properties
131- if ( field . type === 'select' || field . type === 'lookup' ) {
131+ if ( field . type === 'select' || field . type === 'lookup' || field . type === 'master_detail' ) {
132132 formField . options = field . options || [ ] ;
133+ formField . multiple = field . multiple ;
133134 }
134135
135- if ( field . type === 'number' || field . type === 'currency' ) {
136+ if ( field . type === 'number' || field . type === 'currency' || field . type === 'percent' ) {
136137 formField . min = field . min ;
137138 formField . max = field . max ;
138139 formField . step = field . precision ? Math . pow ( 10 , - field . precision ) : undefined ;
139140 }
140141
141- if ( field . type === 'text' || field . type === 'textarea' ) {
142- formField . maxLength = field . maxLength ;
142+ if ( field . type === 'text' || field . type === 'textarea' || field . type === 'markdown' || field . type === 'html' ) {
143+ formField . maxLength = field . max_length ;
144+ formField . minLength = field . min_length ;
145+ }
146+
147+ if ( field . type === 'file' || field . type === 'image' ) {
148+ formField . multiple = field . multiple ;
149+ formField . accept = field . accept ? field . accept . join ( ',' ) : undefined ;
150+ // Add validation hints for file size and dimensions
151+ if ( field . max_size ) {
152+ const sizeHint = `Max size: ${ formatFileSize ( field . max_size ) } ` ;
153+ formField . description = formField . description
154+ ? `${ formField . description } (${ sizeHint } )`
155+ : sizeHint ;
156+ }
157+ }
158+
159+ if ( field . type === 'email' ) {
160+ formField . inputType = 'email' ;
161+ }
162+
163+ if ( field . type === 'phone' ) {
164+ formField . inputType = 'tel' ;
165+ }
166+
167+ if ( field . type === 'url' ) {
168+ formField . inputType = 'url' ;
169+ }
170+
171+ if ( field . type === 'password' ) {
172+ formField . inputType = 'password' ;
173+ }
174+
175+ if ( field . type === 'time' ) {
176+ formField . inputType = 'time' ;
177+ }
178+
179+ // Read-only fields for computed types
180+ if ( field . type === 'formula' || field . type === 'summary' || field . type === 'auto_number' ) {
181+ formField . disabled = true ;
143182 }
144183
145184 generatedFields . push ( formField ) ;
@@ -245,29 +284,92 @@ export const ObjectForm: React.FC<ObjectFormProps> = ({
245284 * `select`). If a field type is not explicitly mapped, the function falls
246285 * back to the generic `"input"` type.
247286 *
287+ * Updated to support all field types from @objectql/types v3.0.1:
288+ * text, textarea, markdown, html, select, date, datetime, time, number,
289+ * currency, percent, boolean, email, phone, url, image, file, location,
290+ * lookup, master_detail, password, formula, summary, auto_number, object,
291+ * vector, grid
292+ *
248293 * @param fieldType - The ObjectQL field type identifier to convert
249294 * (for example: `"text"`, `"number"`, `"date"`, `"lookup"`).
250295 * @returns The normalized form field type string used in the form schema
251296 * (for example: `"input"`, `"textarea"`, `"date-picker"`, `"select"`).
252297 */
253298function mapFieldTypeToFormType ( fieldType : string ) : string {
254299 const typeMap : Record < string , string > = {
300+ // Text-based fields
255301 text : 'input' ,
256302 textarea : 'textarea' ,
303+ markdown : 'textarea' , // Markdown editor (fallback to textarea)
304+ html : 'textarea' , // Rich text editor (fallback to textarea)
305+
306+ // Numeric fields
257307 number : 'input' ,
258308 currency : 'input' ,
259309 percent : 'input' ,
310+
311+ // Date/Time fields
260312 date : 'date-picker' ,
261313 datetime : 'date-picker' ,
314+ time : 'input' , // Time picker (fallback to input with type="time")
315+
316+ // Boolean
262317 boolean : 'switch' ,
318+
319+ // Selection fields
263320 select : 'select' ,
321+ lookup : 'select' ,
322+ master_detail : 'select' ,
323+
324+ // Contact fields
264325 email : 'input' ,
326+ phone : 'input' ,
265327 url : 'input' ,
328+
329+ // File fields
330+ file : 'file-upload' ,
331+ image : 'file-upload' ,
332+
333+ // Special fields
266334 password : 'input' ,
267- lookup : 'select' ,
268- master_detail : 'select' ,
269- fileupload : 'file-upload' ,
335+ location : 'input' , // Location/map field (fallback to input)
336+
337+ // Auto-generated/computed fields (typically read-only)
338+ formula : 'input' ,
339+ summary : 'input' ,
340+ auto_number : 'input' ,
341+
342+ // Complex data types
343+ object : 'input' , // JSON object (fallback to input)
344+ vector : 'input' , // Vector/embedding data (fallback to input)
345+ grid : 'input' , // Grid/table data (fallback to input)
270346 } ;
271347
272348 return typeMap [ fieldType ] || 'input' ;
273349}
350+
351+ /**
352+ * Formats file size in bytes to human-readable string
353+ * @param bytes - File size in bytes (must be non-negative)
354+ * @returns Formatted string (e.g., "5 MB", "1.5 GB")
355+ */
356+ function formatFileSize ( bytes : number ) : string {
357+ if ( bytes < 0 || ! Number . isFinite ( bytes ) ) {
358+ return '0 B' ;
359+ }
360+
361+ if ( bytes === 0 ) {
362+ return '0 B' ;
363+ }
364+
365+ const units = [ 'B' , 'KB' , 'MB' , 'GB' , 'TB' ] ;
366+ let size = bytes ;
367+ let unitIndex = 0 ;
368+
369+ while ( size >= 1024 && unitIndex < units . length - 1 ) {
370+ size /= 1024 ;
371+ unitIndex ++ ;
372+ }
373+
374+ return `${ size . toFixed ( unitIndex > 0 ? 1 : 0 ) } ${ units [ unitIndex ] } ` ;
375+ }
0 commit comments