From afc5489fe0be0ab20abc8d2820d6b099c455a975 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 10:42:21 +0000 Subject: [PATCH 1/4] Initial plan From fcb4a7491c03e77ba607f588439bcbd21ba3643b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 10:48:31 +0000 Subject: [PATCH 2/4] Update ObjectForm and ObjectTable components to support all ObjectQL v3.0.1 field types Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- packages/plugin-object/src/ObjectForm.tsx | 105 +++++++++++++++++++-- packages/plugin-object/src/ObjectTable.tsx | 33 +++++++ 2 files changed, 131 insertions(+), 7 deletions(-) diff --git a/packages/plugin-object/src/ObjectForm.tsx b/packages/plugin-object/src/ObjectForm.tsx index e3dcc9cf..8a797704 100644 --- a/packages/plugin-object/src/ObjectForm.tsx +++ b/packages/plugin-object/src/ObjectForm.tsx @@ -128,18 +128,54 @@ export const ObjectForm: React.FC = ({ }; // Add field-specific properties - if (field.type === 'select' || field.type === 'lookup') { + if (field.type === 'select' || field.type === 'lookup' || field.type === 'master_detail') { formField.options = field.options || []; + formField.multiple = field.multiple; } - if (field.type === 'number' || field.type === 'currency') { + if (field.type === 'number' || field.type === 'currency' || field.type === 'percent') { formField.min = field.min; formField.max = field.max; formField.step = field.precision ? Math.pow(10, -field.precision) : undefined; } - if (field.type === 'text' || field.type === 'textarea') { - formField.maxLength = field.maxLength; + if (field.type === 'text' || field.type === 'textarea' || field.type === 'markdown' || field.type === 'html') { + formField.maxLength = field.max_length; + formField.minLength = field.min_length; + } + + if (field.type === 'file' || field.type === 'image') { + formField.multiple = field.multiple; + formField.accept = field.accept?.join(','); + // Add validation hints for file size and dimensions + if (field.max_size) { + formField.description = (formField.description || '') + ` (Max size: ${formatFileSize(field.max_size)})`; + } + } + + if (field.type === 'email') { + formField.inputType = 'email'; + } + + if (field.type === 'phone') { + formField.inputType = 'tel'; + } + + if (field.type === 'url') { + formField.inputType = 'url'; + } + + if (field.type === 'password') { + formField.inputType = 'password'; + } + + if (field.type === 'time') { + formField.inputType = 'time'; + } + + // Read-only fields for computed types + if (field.type === 'formula' || field.type === 'summary' || field.type === 'auto_number') { + formField.disabled = true; } generatedFields.push(formField); @@ -245,6 +281,12 @@ export const ObjectForm: React.FC = ({ * `select`). If a field type is not explicitly mapped, the function falls * back to the generic `"input"` type. * + * Updated to support all field types from @objectql/types v3.0.1: + * text, textarea, markdown, html, select, date, datetime, time, number, + * currency, percent, boolean, email, phone, url, image, file, location, + * lookup, master_detail, password, formula, summary, auto_number, object, + * vector, grid + * * @param fieldType - The ObjectQL field type identifier to convert * (for example: `"text"`, `"number"`, `"date"`, `"lookup"`). * @returns The normalized form field type string used in the form schema @@ -252,22 +294,71 @@ export const ObjectForm: React.FC = ({ */ function mapFieldTypeToFormType(fieldType: string): string { const typeMap: Record = { + // Text-based fields text: 'input', textarea: 'textarea', + markdown: 'textarea', // Markdown editor (fallback to textarea) + html: 'textarea', // Rich text editor (fallback to textarea) + + // Numeric fields number: 'input', currency: 'input', percent: 'input', + + // Date/Time fields date: 'date-picker', datetime: 'date-picker', + time: 'input', // Time picker (fallback to input with type="time") + + // Boolean boolean: 'switch', + + // Selection fields select: 'select', + lookup: 'select', + master_detail: 'select', + + // Contact fields email: 'input', + phone: 'input', url: 'input', + + // File fields + file: 'file-upload', + image: 'file-upload', + + // Special fields password: 'input', - lookup: 'select', - master_detail: 'select', - fileupload: 'file-upload', + location: 'input', // Location/map field (fallback to input) + + // Auto-generated/computed fields (typically read-only) + formula: 'input', + summary: 'input', + auto_number: 'input', + + // Complex data types + object: 'input', // JSON object (fallback to input) + vector: 'input', // Vector/embedding data (fallback to input) + grid: 'input', // Grid/table data (fallback to input) }; return typeMap[fieldType] || 'input'; } + +/** + * Formats file size in bytes to human-readable string + * @param bytes - File size in bytes + * @returns Formatted string (e.g., "5 MB", "1.5 GB") + */ +function formatFileSize(bytes: number): string { + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let size = bytes; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + + return `${size.toFixed(unitIndex > 0 ? 1 : 0)} ${units[unitIndex]}`; +} diff --git a/packages/plugin-object/src/ObjectTable.tsx b/packages/plugin-object/src/ObjectTable.tsx index ac80b451..585e9cef 100644 --- a/packages/plugin-object/src/ObjectTable.tsx +++ b/packages/plugin-object/src/ObjectTable.tsx @@ -96,6 +96,39 @@ export const ObjectTable: React.FC = ({ accessorKey: fieldName, }; + // Add field type-specific formatting hints + if (field.type === 'date' || field.type === 'datetime') { + column.type = 'date'; + } else if (field.type === 'boolean') { + column.type = 'boolean'; + } else if (field.type === 'number' || field.type === 'currency' || field.type === 'percent') { + column.type = 'number'; + } else if (field.type === 'image' || field.type === 'file') { + // For file/image fields, display the name or count + column.cell = (value: any) => { + if (!value) return '-'; + if (Array.isArray(value)) { + return `${value.length} ${field.type}(s)`; + } + return value.name || value.original_name || 'File'; + }; + } else if (field.type === 'lookup' || field.type === 'master_detail') { + // For relationship fields, display the name property if available + column.cell = (value: any) => { + if (!value) return '-'; + if (typeof value === 'object') { + return value.name || value.label || value._id || String(value); + } + return String(value); + }; + } else if (field.type === 'url') { + // For URL fields, make them clickable + column.cell = (value: any) => { + if (!value) return '-'; + return value; // The table renderer should handle URL formatting + }; + } + generatedColumns.push(column); } }); From fb081fa3a8bb2d445b5d11bdf0d71e101136d189 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 10:52:46 +0000 Subject: [PATCH 3/4] Address code review feedback - improve error handling and string formatting Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- packages/plugin-object/src/ObjectForm.tsx | 15 +++++++++++++-- packages/plugin-object/src/ObjectTable.tsx | 8 +++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/plugin-object/src/ObjectForm.tsx b/packages/plugin-object/src/ObjectForm.tsx index 8a797704..6bd28e6b 100644 --- a/packages/plugin-object/src/ObjectForm.tsx +++ b/packages/plugin-object/src/ObjectForm.tsx @@ -149,7 +149,10 @@ export const ObjectForm: React.FC = ({ formField.accept = field.accept?.join(','); // Add validation hints for file size and dimensions if (field.max_size) { - formField.description = (formField.description || '') + ` (Max size: ${formatFileSize(field.max_size)})`; + const sizeHint = `Max size: ${formatFileSize(field.max_size)}`; + formField.description = formField.description + ? `${formField.description} (${sizeHint})` + : sizeHint; } } @@ -347,10 +350,18 @@ function mapFieldTypeToFormType(fieldType: string): string { /** * Formats file size in bytes to human-readable string - * @param bytes - File size in bytes + * @param bytes - File size in bytes (must be non-negative) * @returns Formatted string (e.g., "5 MB", "1.5 GB") */ function formatFileSize(bytes: number): string { + if (bytes < 0 || !Number.isFinite(bytes)) { + return '0 B'; + } + + if (bytes === 0) { + return '0 B'; + } + const units = ['B', 'KB', 'MB', 'GB', 'TB']; let size = bytes; let unitIndex = 0; diff --git a/packages/plugin-object/src/ObjectTable.tsx b/packages/plugin-object/src/ObjectTable.tsx index 585e9cef..c6a1dcee 100644 --- a/packages/plugin-object/src/ObjectTable.tsx +++ b/packages/plugin-object/src/ObjectTable.tsx @@ -108,7 +108,9 @@ export const ObjectTable: React.FC = ({ column.cell = (value: any) => { if (!value) return '-'; if (Array.isArray(value)) { - return `${value.length} ${field.type}(s)`; + const count = value.length; + const fileType = field.type === 'image' ? 'image' : 'file'; + return count === 1 ? `1 ${fileType}` : `${count} ${fileType}s`; } return value.name || value.original_name || 'File'; }; @@ -116,8 +118,8 @@ export const ObjectTable: React.FC = ({ // For relationship fields, display the name property if available column.cell = (value: any) => { if (!value) return '-'; - if (typeof value === 'object') { - return value.name || value.label || value._id || String(value); + if (typeof value === 'object' && value !== null) { + return value.name || value.label || value._id || JSON.stringify(value); } return String(value); }; From d82029d7d46c340cb88c81ef3896a98b493bda4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 17 Jan 2026 10:54:06 +0000 Subject: [PATCH 4/4] Final improvements: explicit undefined handling and safer object fallbacks Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- packages/plugin-object/src/ObjectForm.tsx | 2 +- packages/plugin-object/src/ObjectTable.tsx | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/plugin-object/src/ObjectForm.tsx b/packages/plugin-object/src/ObjectForm.tsx index 6bd28e6b..dabea10b 100644 --- a/packages/plugin-object/src/ObjectForm.tsx +++ b/packages/plugin-object/src/ObjectForm.tsx @@ -146,7 +146,7 @@ export const ObjectForm: React.FC = ({ if (field.type === 'file' || field.type === 'image') { formField.multiple = field.multiple; - formField.accept = field.accept?.join(','); + formField.accept = field.accept ? field.accept.join(',') : undefined; // Add validation hints for file size and dimensions if (field.max_size) { const sizeHint = `Max size: ${formatFileSize(field.max_size)}`; diff --git a/packages/plugin-object/src/ObjectTable.tsx b/packages/plugin-object/src/ObjectTable.tsx index c6a1dcee..6609a250 100644 --- a/packages/plugin-object/src/ObjectTable.tsx +++ b/packages/plugin-object/src/ObjectTable.tsx @@ -119,7 +119,12 @@ export const ObjectTable: React.FC = ({ column.cell = (value: any) => { if (!value) return '-'; if (typeof value === 'object' && value !== null) { - return value.name || value.label || value._id || JSON.stringify(value); + // Try common display properties first + if (value.name) return value.name; + if (value.label) return value.label; + if (value._id) return value._id; + // Fallback to object type indicator + return '[Object]'; } return String(value); };