Skip to content

Commit fcb4a74

Browse files
Copilothuangyiirene
andcommitted
Update ObjectForm and ObjectTable components to support all ObjectQL v3.0.1 field types
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
1 parent afc5489 commit fcb4a74

File tree

2 files changed

+131
-7
lines changed

2 files changed

+131
-7
lines changed

packages/plugin-object/src/ObjectForm.tsx

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,54 @@ 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?.join(',');
150+
// Add validation hints for file size and dimensions
151+
if (field.max_size) {
152+
formField.description = (formField.description || '') + ` (Max size: ${formatFileSize(field.max_size)})`;
153+
}
154+
}
155+
156+
if (field.type === 'email') {
157+
formField.inputType = 'email';
158+
}
159+
160+
if (field.type === 'phone') {
161+
formField.inputType = 'tel';
162+
}
163+
164+
if (field.type === 'url') {
165+
formField.inputType = 'url';
166+
}
167+
168+
if (field.type === 'password') {
169+
formField.inputType = 'password';
170+
}
171+
172+
if (field.type === 'time') {
173+
formField.inputType = 'time';
174+
}
175+
176+
// Read-only fields for computed types
177+
if (field.type === 'formula' || field.type === 'summary' || field.type === 'auto_number') {
178+
formField.disabled = true;
143179
}
144180

145181
generatedFields.push(formField);
@@ -245,29 +281,84 @@ export const ObjectForm: React.FC<ObjectFormProps> = ({
245281
* `select`). If a field type is not explicitly mapped, the function falls
246282
* back to the generic `"input"` type.
247283
*
284+
* Updated to support all field types from @objectql/types v3.0.1:
285+
* text, textarea, markdown, html, select, date, datetime, time, number,
286+
* currency, percent, boolean, email, phone, url, image, file, location,
287+
* lookup, master_detail, password, formula, summary, auto_number, object,
288+
* vector, grid
289+
*
248290
* @param fieldType - The ObjectQL field type identifier to convert
249291
* (for example: `"text"`, `"number"`, `"date"`, `"lookup"`).
250292
* @returns The normalized form field type string used in the form schema
251293
* (for example: `"input"`, `"textarea"`, `"date-picker"`, `"select"`).
252294
*/
253295
function mapFieldTypeToFormType(fieldType: string): string {
254296
const typeMap: Record<string, string> = {
297+
// Text-based fields
255298
text: 'input',
256299
textarea: 'textarea',
300+
markdown: 'textarea', // Markdown editor (fallback to textarea)
301+
html: 'textarea', // Rich text editor (fallback to textarea)
302+
303+
// Numeric fields
257304
number: 'input',
258305
currency: 'input',
259306
percent: 'input',
307+
308+
// Date/Time fields
260309
date: 'date-picker',
261310
datetime: 'date-picker',
311+
time: 'input', // Time picker (fallback to input with type="time")
312+
313+
// Boolean
262314
boolean: 'switch',
315+
316+
// Selection fields
263317
select: 'select',
318+
lookup: 'select',
319+
master_detail: 'select',
320+
321+
// Contact fields
264322
email: 'input',
323+
phone: 'input',
265324
url: 'input',
325+
326+
// File fields
327+
file: 'file-upload',
328+
image: 'file-upload',
329+
330+
// Special fields
266331
password: 'input',
267-
lookup: 'select',
268-
master_detail: 'select',
269-
fileupload: 'file-upload',
332+
location: 'input', // Location/map field (fallback to input)
333+
334+
// Auto-generated/computed fields (typically read-only)
335+
formula: 'input',
336+
summary: 'input',
337+
auto_number: 'input',
338+
339+
// Complex data types
340+
object: 'input', // JSON object (fallback to input)
341+
vector: 'input', // Vector/embedding data (fallback to input)
342+
grid: 'input', // Grid/table data (fallback to input)
270343
};
271344

272345
return typeMap[fieldType] || 'input';
273346
}
347+
348+
/**
349+
* Formats file size in bytes to human-readable string
350+
* @param bytes - File size in bytes
351+
* @returns Formatted string (e.g., "5 MB", "1.5 GB")
352+
*/
353+
function formatFileSize(bytes: number): string {
354+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
355+
let size = bytes;
356+
let unitIndex = 0;
357+
358+
while (size >= 1024 && unitIndex < units.length - 1) {
359+
size /= 1024;
360+
unitIndex++;
361+
}
362+
363+
return `${size.toFixed(unitIndex > 0 ? 1 : 0)} ${units[unitIndex]}`;
364+
}

packages/plugin-object/src/ObjectTable.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,39 @@ export const ObjectTable: React.FC<ObjectTableProps> = ({
9696
accessorKey: fieldName,
9797
};
9898

99+
// Add field type-specific formatting hints
100+
if (field.type === 'date' || field.type === 'datetime') {
101+
column.type = 'date';
102+
} else if (field.type === 'boolean') {
103+
column.type = 'boolean';
104+
} else if (field.type === 'number' || field.type === 'currency' || field.type === 'percent') {
105+
column.type = 'number';
106+
} else if (field.type === 'image' || field.type === 'file') {
107+
// For file/image fields, display the name or count
108+
column.cell = (value: any) => {
109+
if (!value) return '-';
110+
if (Array.isArray(value)) {
111+
return `${value.length} ${field.type}(s)`;
112+
}
113+
return value.name || value.original_name || 'File';
114+
};
115+
} else if (field.type === 'lookup' || field.type === 'master_detail') {
116+
// For relationship fields, display the name property if available
117+
column.cell = (value: any) => {
118+
if (!value) return '-';
119+
if (typeof value === 'object') {
120+
return value.name || value.label || value._id || String(value);
121+
}
122+
return String(value);
123+
};
124+
} else if (field.type === 'url') {
125+
// For URL fields, make them clickable
126+
column.cell = (value: any) => {
127+
if (!value) return '-';
128+
return value; // The table renderer should handle URL formatting
129+
};
130+
}
131+
99132
generatedColumns.push(column);
100133
}
101134
});

0 commit comments

Comments
 (0)