-
Notifications
You must be signed in to change notification settings - Fork 0
Update ObjectQL components to support all v3.0.1 field types #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
afc5489
fcb4a74
fb081fa
d82029d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -128,18 +128,57 @@ export const ObjectForm: React.FC<ObjectFormProps> = ({ | |||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
| // 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 ? field.accept.join(',') : undefined; | ||||||||||||||||
| // Add validation hints for file size and dimensions | ||||||||||||||||
| if (field.max_size) { | ||||||||||||||||
| const sizeHint = `Max size: ${formatFileSize(field.max_size)}`; | ||||||||||||||||
| formField.description = formField.description | ||||||||||||||||
| ? `${formField.description} (${sizeHint})` | ||||||||||||||||
| : sizeHint; | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| 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,29 +284,92 @@ export const ObjectForm: React.FC<ObjectFormProps> = ({ | |||||||||||||||
| * `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 | ||||||||||||||||
| * (for example: `"input"`, `"textarea"`, `"date-picker"`, `"select"`). | ||||||||||||||||
| */ | ||||||||||||||||
| function mapFieldTypeToFormType(fieldType: string): string { | ||||||||||||||||
| const typeMap: Record<string, string> = { | ||||||||||||||||
| // 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 (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'; | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+362
to
+363
|
||||||||||||||||
| return '0 B'; | |
| } | |
| const decimals = unitIndex > 0 ? 1 : 0; | |
| const formattedSize = Number(size.toFixed(decimals)); | |
| return `${formattedSize} ${units[unitIndex]}`; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -96,6 +96,46 @@ | |||||
| 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)) { | ||||||
| const count = value.length; | ||||||
|
||||||
| const count = value.length; | |
| return `${value.length} ${field.type}${value.length !== 1 ? 's' : ''}`; |
Check warning
Code scanning / CodeQL
Comparison between inconvertible types Warning
an expression
Copilot Autofix
AI 9 days ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String concatenation for description updates could lead to duplicate size hints if this logic runs multiple times. Consider checking if the description already contains the size hint before appending, or use a more structured approach to store metadata separately from user-facing descriptions.