Skip to content

Add metadata-driven ObjectGridTable and ObjectForm components with automatic field type recognition#33

Merged
hotlong merged 23 commits intomainfrom
copilot/add-ag-grid-field-type-recognition
Jan 12, 2026
Merged

Add metadata-driven ObjectGridTable and ObjectForm components with automatic field type recognition#33
hotlong merged 23 commits intomainfrom
copilot/add-ag-grid-field-type-recognition

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 12, 2026

Code Review Fixes - Final Update ✅

object-form.tsx

  • Fixed custom regex precedence issue (Comment #2680739153)

    • Custom regex now takes precedence over built-in email/URL validation
    • Built-in patterns only apply when !fieldConfig.regex
    • Prevents user-defined validation from being overwritten
  • Improved email validation regex (Comment #2680739146)

    • Updated to more robust pattern: /^[a-zA-Z0-9.!#$%&'*+/=?^_{|}~-]+@a-zA-Z0-9?(?:.a-zA-Z0-9?)*$/`
    • Handles longer TLDs (not limited to 2 chars)
    • Better validation of domain structure

Previously Fixed (in commit 298ad6e)

  • ✅ useCallback for getHeaders
  • ✅ XSS prevention with sanitization
  • ✅ Date validation with isNaN check
  • ✅ Type safety with RegisterOptions

All code review issues now fully addressed. Build passes ✅

Original prompt

ag-grid 组件应该调用标准的对象元数据接口,应该识别所有的字段类型,会调用用对应的字段控件来显示


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 6 commits January 12, 2026 02:26
…tion

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add field type recognition for ag-grid component Add metadata-driven ObjectGridTable component with automatic field type recognition Jan 12, 2026
Copilot AI requested a review from hotlong January 12, 2026 02:37
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Jan 12, 2026

类似的,开发ObjectForm

@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Jan 12, 2026

类似的,开发ObjectForm

@copilot

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jan 12, 2026

类似的,开发ObjectForm

@copilot

✅ 已完成 ObjectForm 组件开发 (ObjectForm component completed)

Created a metadata-driven form component similar to ObjectGridTable:

Core Features:

  • Auto-generates form fields from ObjectConfig
  • Built-in validation (required, min/max, length, email/URL format, regex)
  • Supports all ObjectQL field types
  • Smart layout (full-width for textarea/markdown)
  • React Hook Form integration

Usage:

import { ObjectForm } from '@objectos/ui';

<ObjectForm
  objectConfig={objectConfig}
  onSubmit={handleSubmit}
  initialValues={data}  // for editing
/>

Documentation:

  • English: OBJECT_FORM.md
  • 中文: OBJECT_FORM_CN.md
  • Example: ObjectFormExample.tsx

Commit: 7a924e1

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title Add metadata-driven ObjectGridTable component with automatic field type recognition Add metadata-driven ObjectGridTable and ObjectForm components with automatic field type recognition Jan 12, 2026
@hotlong hotlong requested review from Copilot and removed request for hotlong January 12, 2026 02:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds metadata-driven UI components to ObjectOS, enabling automatic generation of AG Grid tables and forms from ObjectQL metadata. The implementation eliminates the need for manual column and field configuration by recognizing field types and applying appropriate renderers and validation.

Changes:

  • Added ObjectGridTable component with automatic column generation and type-specific cell renderers for all ObjectQL field types
  • Added ObjectForm component with automatic field generation and validation based on metadata configuration
  • Integrated @objectql/types dependency for type consistency across the platform

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
packages/ui/src/components/object-grid-table.tsx Core AG Grid component with 8 specialized cell renderers for boolean, date, number, currency, percent, select, lookup, email, and URL field types
packages/ui/src/components/object-form.tsx Metadata-driven form component with React Hook Form integration and automatic validation rule generation
packages/ui/src/components/examples/ObjectGridTableExample.tsx Demo component showcasing all field type renderers with sample data
packages/ui/src/components/examples/ObjectFormExample.tsx Demo component demonstrating form creation and editing modes
apps/web/src/components/dashboard/EnhancedObjectListView.tsx Production example integrating ObjectGridTable with API data fetching
packages/ui/src/index.ts Export statements for new components and types
packages/ui/package.json Added @objectql/types dependency
packages/ui/OBJECT_GRID_TABLE.md Comprehensive English documentation for ObjectGridTable
packages/ui/OBJECT_GRID_TABLE_CN.md Chinese documentation for ObjectGridTable
packages/ui/OBJECT_FORM.md Comprehensive English documentation for ObjectForm
packages/ui/OBJECT_FORM_CN.md Chinese documentation for ObjectForm
docs/OBJECT_GRID_INTEGRATION.md Integration guide for adopting ObjectGridTable
IMPLEMENTATION_SUMMARY.md Technical implementation summary
OBJECTFORM_IMPLEMENTATION.md ObjectForm implementation summary

Comment on lines +21 to +28
const getHeaders = () => {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
// Use the actual user ID from props instead of hard-coded value
if (user?.id || user?._id) {
headers['x-user-id'] = user.id || user._id;
}
return headers;
};
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getHeaders function is recreated on every render, and is used as a dependency in useEffect hooks. This can cause the useEffect hooks to run more often than necessary because the function reference changes on each render. Move getHeaders outside the component or wrap it in useCallback to prevent unnecessary re-fetches of metadata and data.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed all code review issues in commit 298ad6e:

  • getHeaders now wrapped in useCallback to prevent unnecessary re-renders
  • ✅ Dependencies updated to use getHeaders instead of user
  • ✅ Added XSS prevention with email/URL sanitization
  • ✅ Added date validation with isNaN check
  • ✅ Improved type safety (RegisterOptions, better object checks)

All changes tested and building successfully.

Comment on lines +204 to +242
const EmailCellRenderer = (props: ICellRendererParams) => {
const { value } = props

if (!value) {
return <span className="text-muted-foreground">-</span>
}

return (
<a
href={`mailto:${value}`}
className="text-primary hover:underline"
onClick={(e) => e.stopPropagation()}
>
{value}
</a>
)
}

/**
* Cell renderer for URL fields
*/
const UrlCellRenderer = (props: ICellRendererParams) => {
const { value } = props

if (!value) {
return <span className="text-muted-foreground">-</span>
}

return (
<a
href={value}
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
onClick={(e) => e.stopPropagation()}
>
{value}
</a>
)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The email and URL renderers create links without sanitizing the input values. For email fields, a malicious value could potentially inject JavaScript through the mailto: protocol (e.g., javascript:alert('XSS')). For URL fields, while the code requires http:// or https://, there's no validation of the actual URL content before rendering it as a link. Consider validating/sanitizing these values before using them in href attributes to prevent potential XSS attacks.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 298ad6e with sanitization functions:

  • sanitizeEmail(): Blocks javascript: protocol injection
  • sanitizeUrl(): Only allows http/https, blocks dangerous protocols

Comment on lines +90 to +114
try {
const date = value instanceof Date ? value : new Date(value)
// Format based on field type from colDef
const extendedColDef = colDef as ExtendedColDef
const fieldType = extendedColDef.fieldType

if (fieldType === 'datetime') {
return (
<div className="flex items-center gap-1.5">
<CalendarIcon className="w-3 h-3 text-muted-foreground" />
<span>{format(date, "PPp")}</span>
</div>
)
}

return (
<div className="flex items-center gap-1.5">
<CalendarIcon className="w-3 h-3 text-muted-foreground" />
<span>{format(date, "PP")}</span>
</div>
)
} catch (e) {
return <span className="text-muted-foreground">{String(value)}</span>
}
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DateCellRenderer does not handle invalid dates gracefully. If the date parsing fails (caught in the try-catch), it displays the raw value as a string. However, there's no validation to check if the Date object is actually valid after construction. JavaScript's Date constructor can create "Invalid Date" objects that won't throw an error but will cause the format function to throw. Consider checking isNaN(date.getTime()) after creating the Date object to ensure it's valid before calling format.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 298ad6e - added isNaN(date.getTime()) check before formatting to handle Invalid Date objects gracefully.

* Generate validation rules for react-hook-form based on field config
*/
function getValidationRules(fieldConfig: FieldConfig) {
const rules: any = {}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation rules object uses any type instead of a more specific type from react-hook-form. This reduces type safety and could lead to runtime errors if incorrect validation rules are constructed. Consider using the proper type from react-hook-form, such as RegisterOptions or a custom interface that extends it, to ensure type safety for the validation rules.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 298ad6e - changed type from any to RegisterOptions from react-hook-form for better type safety.

Comment on lines +117 to +119
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The email validation regex may not handle all valid email formats correctly. The pattern /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i does not allow for top-level domains longer than the minimum of 2 characters, but some newer TLDs can be longer. Additionally, it doesn't handle special cases like multiple consecutive dots or trailing dots in the domain, which could lead to false positives. Consider using a more robust email validation pattern or a library like validator.js for more comprehensive email validation.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 3ae142d - improved email regex to handle longer TLDs and better domain validation:
/^[a-zA-Z0-9.!#$%&'*+/=?^_{|}~-]+@a-zA-Z0-9?(?:.a-zA-Z0-9?)*$/`

Comment on lines +107 to +128
if (fieldConfig.regex) {
rules.pattern = {
value: new RegExp(fieldConfig.regex),
message: 'Invalid format'
}
}

// Email validation
if (fieldConfig.type === 'email') {
rules.pattern = {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
}

// URL validation
if (fieldConfig.type === 'url') {
rules.pattern = {
value: /^https?:\/\/.+/i,
message: 'Invalid URL (must start with http:// or https://)'
}
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a field has type 'email' or 'url' AND a custom regex pattern is defined, the custom regex will be overwritten by the built-in email/URL pattern. The code should check if a custom regex already exists and either combine the validations or give precedence to the custom regex. This could break user expectations if they define a custom regex for stricter validation on email or URL fields.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 3ae142d - custom regex now takes precedence. Built-in email/URL patterns only apply when !fieldConfig.regex is true.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
hotlong and others added 3 commits January 12, 2026 11:58
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Deleted OBJECT_FORM_CN.md and OBJECT_GRID_TABLE_CN.md documentation files. Moved example files to a new location. Added @objectql/types dependency to apps/web/package.json.
Enhanced the authentication client to support PostgreSQL, MongoDB, and SQLite databases. Updated configuration to use a file-based SQLite database by default. Added 'better-sqlite3' and 'mongodb' dependencies, and improved user role assignment logic for all supported databases.
@hotlong hotlong marked this pull request as ready for review January 12, 2026 06:16
@hotlong hotlong merged commit ebd639c into main Jan 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants