Uh oh!
There was an error while loading. Please reload this page.
Add actions and form_layout sections to CRM example metadata - #660
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
hotlong
commented
Feb 20, 2026
元数据应该分拆成单独的文件 @copilot |
…c/actions/ Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Done in 9ca0076. Split metadata into per-object files:
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Adds missing CRM example metadata to better exercise Console features end-to-end by introducing stack-level actions and form section layouts, and organizing the metadata into per-object modules.
Changes:
- Added per-object
views/*modules that includeform.sections(form layout) and multiplelistViews. - Added per-object
actions/*modules and registered them in the CRM stack config. - Updated exports and ROADMAP entry to reflect the enrichment.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
| examples/crm/objectstack.config.ts | Imports new per-object view/action modules; registers actions and swaps inline views for module references. |
| examples/crm/src/index.ts | Re-exports newly added views/* and actions/* modules for easier consumption. |
| examples/crm/src/views/account.view.ts | Adds form.sections layout and maintains 2 list views for Account. |
| examples/crm/src/views/contact.view.ts | Adds form.sections layout and maintains 2 list views for Contact. |
| examples/crm/src/views/opportunity.view.ts | Adds form.sections layout and multiple list views including kanban pipeline. |
| examples/crm/src/views/product.view.ts | Adds form.sections layout and maintains 2 list views for Product. |
| examples/crm/src/views/order.view.ts | Adds form.sections layout and maintains 2 list views for Order. |
| examples/crm/src/views/user.view.ts | Adds form.sections layout and maintains 2 list views for User. |
| examples/crm/src/views/event.view.ts | Adds form.sections layout and list views including a calendar view. |
| examples/crm/src/views/project.view.ts | Adds form.sections layout and list views including gantt/timeline entries. |
| examples/crm/src/actions/account.actions.ts | Introduces Account actions with confirm/success metadata and parameter definitions. |
| examples/crm/src/actions/contact.actions.ts | Introduces Contact actions with confirm/success metadata and parameter definitions. |
| examples/crm/src/actions/opportunity.actions.ts | Introduces Opportunity actions with confirm/success metadata and parameter definitions. |
| examples/crm/src/actions/product.actions.ts | Introduces Product actions with parameter definitions. |
| examples/crm/src/actions/order.actions.ts | Introduces Order actions with confirm/success metadata and parameter definitions. |
| examples/crm/src/actions/user.actions.ts | Introduces User actions with confirm/success metadata. |
| examples/crm/src/actions/project.actions.ts | Introduces Project Task actions with parameter definitions. |
| examples/crm/src/actions/event.actions.ts | Introduces Event actions with confirm/success metadata and parameter definitions. |
| ROADMAP.md | Updates the CRM enrichment entry to include actions + form_layout sections. |
| name: 'account_send_email', | ||
| label: 'Send Email', | ||
| icon: 'mail', | ||
| type: 'api' as const, | ||
| locations: ['record_header' as const, 'list_item' as const], | ||
| params: [ | ||
| { name: 'to', label: 'To Email', type: 'email' as const, required: true }, | ||
| { name: 'subject', label: 'Subject', type: 'text' as const, required: true }, |
There was a problem hiding this comment.
These actions are declared as type: 'api' but don't provide endpoint/api/target. In the current ActionRunner implementation, type: 'api' without an endpoint returns No API endpoint provided, so clicking these will always fail. Consider either supplying a dummy endpoint (if you want to exercise API execution) or switching these example actions to a non-network type (e.g. script) so Console verification can reach success/refresh flows.
| locations: ['record_header' as const, 'list_item' as const], | ||
| params: [ | ||
| { | ||
| name: 'new_stage', label: 'New Stage', type: 'select' as const, required: true, | ||
| options: [ | ||
| { label: 'Prospecting', value: 'prospecting' }, | ||
| { label: 'Qualification', value: 'qualification' }, | ||
| { label: 'Proposal', value: 'proposal' }, | ||
| { label: 'Negotiation', value: 'negotiation' }, | ||
| { label: 'Closed Won', value: 'closed_won' }, | ||
| { label: 'Closed Lost', value: 'closed_lost' }, | ||
| ], | ||
| }, | ||
| ], | ||
| refreshAfter: true, | ||
| successMessage: 'Stage updated successfully', | ||
| }, |
There was a problem hiding this comment.
params: [...] is used here as parameter schema, but ActionRunner expects actionParams for param collection and uses params for runtime values/body. As-is, the param collection dialog won’t trigger. Align with the runner (or add a translation step in the Console executor).
| locations: ['record_header' as const, 'list_item' as const], | ||
| params: [ | ||
| { | ||
| name: 'new_status', label: 'New Status', type: 'select' as const, required: true, | ||
| options: [ | ||
| { label: 'Draft', value: 'draft' }, | ||
| { label: 'Pending', value: 'pending' }, | ||
| { label: 'Paid', value: 'paid' }, | ||
| { label: 'Shipped', value: 'shipped' }, | ||
| { label: 'Delivered', value: 'delivered' }, | ||
| { label: 'Cancelled', value: 'cancelled' }, | ||
| ], | ||
| }, | ||
| ], | ||
| refreshAfter: true, | ||
| successMessage: 'Order status updated', |
There was a problem hiding this comment.
params: [...] is being used as parameter definitions, but ActionRunner’s param dialog is driven by actionParams and treats params as runtime values. If you want to exercise param collection, align the field name or translate it before execution.
| locations: ['record_header' as const], | ||
| params: [ | ||
| { name: 'message', label: 'Optional Message', type: 'textarea' as const }, | ||
| ], | ||
| successMessage: 'Invitation sent to all participants', | ||
| }, |
There was a problem hiding this comment.
params: [...] is used as parameter definitions here, but ActionRunner’s param collection is keyed off actionParams and treats params as runtime values. To actually exercise param collection in Console, align the schema (or translate spec params → runner actionParams).
| locations: ['record_header' as const, 'list_item' as const], | ||
| params: [ | ||
| { name: 'subject', label: 'Subject', type: 'text' as const, required: true }, | ||
| { name: 'body', label: 'Message', type: 'textarea' as const }, | ||
| ], | ||
| successMessage: 'Email sent successfully', |
There was a problem hiding this comment.
These actions use params: [...] as parameter definitions. In ActionRunner, param collection is driven by actionParams (definitions) and params is treated as runtime values/body. If you want the Console to pop a parameter dialog, either export definitions under actionParams or add a mapping layer that translates spec params into actionParams before execution.
| { | ||
| name: 'user_reset_password', | ||
| label: 'Reset Password', | ||
| icon: 'key', | ||
| type: 'api' as const, | ||
| locations: ['record_header' as const, 'record_more' as const], | ||
| confirmText: 'Send a password reset link to this user?', | ||
| successMessage: 'Password reset email sent', | ||
| }, | ||
| { | ||
| name: 'user_deactivate', | ||
| label: 'Deactivate User', | ||
| icon: 'user-x', | ||
| type: 'api' as const, | ||
| locations: ['record_more' as const], | ||
| variant: 'danger' as const, | ||
| confirmText: 'Are you sure you want to deactivate this user?', | ||
| refreshAfter: true, | ||
| successMessage: 'User deactivated', |
There was a problem hiding this comment.
These actions are type: 'api' but don’t specify endpoint/api/target. In the current ActionRunner this makes execution fail with No API endpoint provided. Add endpoints (mock/placeholder is fine) or switch to an action type that can succeed without a network call.
| label: 'Timeline', | ||
| type: 'timeline' as const, | ||
| data: { provider: 'object' as const, object: 'project_task' }, | ||
| columns: ['name', 'start_date', 'status'], |
There was a problem hiding this comment.
Timeline view defaults in Console use dateField: 'due_date' when no timeline.dateField is provided. project_task doesn’t have due_date, so this Timeline view will render with missing/invalid dates. Add an explicit timeline config (e.g., set the date field to start_date, and optionally endDateField to end_date) so the Timeline view is actually usable.
| columns: ['name','start_date','status'], | |
| columns: ['name','start_date','status'], | |
| timeline: { | |
| dateField: 'start_date', | |
| endDateField: 'end_date', | |
| }, |
| { | ||
| name: 'task_change_status', | ||
| label: 'Change Status', | ||
| icon: 'circle-check', | ||
| type: 'api' as const, | ||
| locations: ['record_header' as const, 'list_item' as const], | ||
| params: [ | ||
| { | ||
| name: 'new_status', label: 'New Status', type: 'select' as const, required: true, | ||
| options: [ | ||
| { label: 'Planned', value: 'planned' }, | ||
| { label: 'In Progress', value: 'in_progress' }, | ||
| { label: 'Completed', value: 'completed' }, | ||
| { label: 'On Hold', value: 'on_hold' }, | ||
| ], | ||
| }, | ||
| ], | ||
| refreshAfter: true, | ||
| successMessage: 'Task status updated', | ||
| }, | ||
| { | ||
| name: 'task_assign', | ||
| label: 'Assign User', | ||
| icon: 'user-plus', | ||
| type: 'api' as const, | ||
| locations: ['record_header' as const, 'list_item' as const], | ||
| params: [ | ||
| { name: 'assignee_id', label: 'Assignee', type: 'lookup' as const, required: true }, | ||
| ], | ||
| refreshAfter: true, | ||
| successMessage: 'Task assigned successfully', | ||
| }, |
There was a problem hiding this comment.
These type: 'api' actions have no endpoint/api/target, so ActionRunner execution will fail with No API endpoint provided. Provide endpoints (even placeholder/mock) or switch to a non-API action type so these actions can actually be run in Console.
| { | ||
| name: 'event_send_invitation', | ||
| label: 'Send Invitation', | ||
| icon: 'send', | ||
| type: 'api' as const, | ||
| locations: ['record_header' as const], | ||
| params: [ | ||
| { name: 'message', label: 'Optional Message', type: 'textarea' as const }, | ||
| ], | ||
| successMessage: 'Invitation sent to all participants', | ||
| }, | ||
| { | ||
| name: 'event_mark_completed', | ||
| label: 'Mark as Completed', | ||
| icon: 'check-circle', | ||
| type: 'api' as const, | ||
| locations: ['record_header' as const], | ||
| confirmText: 'Mark this event as completed?', | ||
| refreshAfter: true, | ||
| successMessage: 'Event marked as completed', | ||
| }, | ||
| { | ||
| name: 'event_cancel', | ||
| label: 'Cancel Event', | ||
| icon: 'x-circle', | ||
| type: 'api' as const, | ||
| locations: ['record_more' as const], | ||
| variant: 'danger' as const, | ||
| params: [ | ||
| { name: 'cancel_reason', label: 'Cancellation Reason', type: 'text' as const }, | ||
| { name: 'notify_participants', label: 'Notify Participants', type: 'boolean' as const }, | ||
| ], | ||
| confirmText: 'Are you sure you want to cancel this event?', | ||
| refreshAfter: true, | ||
| successMessage: 'Event cancelled', | ||
| }, |
There was a problem hiding this comment.
These actions are type: 'api' without any endpoint/api/target. With the current ActionRunner, executing them always fails (No API endpoint provided). Provide endpoints or use a non-API type so the actions can succeed during Console verification.
| closing_this_month: { | ||
| name: 'closing_this_month', | ||
| label: 'Closing This Month', | ||
| type: 'grid' as const, | ||
| data: { provider: 'object' as const, object: 'opportunity' }, | ||
| columns: ['name', 'amount', 'stage', 'close_date', 'probability'], | ||
| sort: [{ field: 'close_date', order: 'asc' as const }], | ||
| }, |
There was a problem hiding this comment.
The closing_this_month view is labeled “Closing This Month” but doesn’t define any filter criteria, so it will show the same records as the unfiltered list (just with a sort). Either add a close_date-based filter (if supported by the spec/runtime) or rename the view label/id to avoid implying date filtering that isn’t happening.
CRM example was missing
actionsandform_layout— two of the three spec capabilities needed for end-to-end Console verification.Actions (21 total)
Top-level
actionsarray added todefineStack()covering all 8 objects:Exercises
params(select/text/email/textarea/boolean/currency/lookup),confirmText,variant(primary/danger),locations(record_header/list_item/record_more),refreshAfter,successMessage.Form Layouts (29 sections across 8 objects)
form.sectionsadded to each view entry:Each object gets 3–4 sections with logical field grouping, multi-column layouts, and collapsible detail sections.
Metadata split into per-object files
Views and actions are organized into separate files following the existing
src/objects/pattern:src/views/— 8 files (account.view.ts,contact.view.ts,opportunity.view.ts,product.view.ts,order.view.ts,user.view.ts,event.view.ts,project.view.ts)src/actions/— 8 files (account.actions.ts,contact.actions.ts,opportunity.actions.ts,product.actions.ts,order.actions.ts,user.actions.ts,event.actions.ts,project.actions.ts)objectstack.config.tsreduced from 1418 → 770 lines, now imports and spreads from separate filessrc/index.tsre-exports all new modulesNote
objectstack compilehas a pre-existing re-validation bug onform.sections.columns(Zod transforms string"2"→ number2, then re-validates against the string enum).defineStack()itself accepts the config correctly.Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.