Skip to content

Add actions and form_layout sections to CRM example metadata - #660

Merged
hotlong merged 4 commits into
mainfrom
copilot/enrich-crm-example-metadata
Feb 20, 2026
Merged

Add actions and form_layout sections to CRM example metadata#660
hotlong merged 4 commits into
mainfrom
copilot/enrich-crm-example-metadata

Conversation

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

CRM example was missing actions and form_layout — two of the three spec capabilities needed for end-to-end Console verification.

Actions (21 total)

Top-level actions array added to defineStack() covering all 8 objects:

  • Account: Send Email, Assign Owner, Merge Accounts
  • Contact: Send Email, Convert to Customer, Log a Call
  • Opportunity: Change Stage, Mark as Won, Mark as Lost
  • Product: Toggle Active, Update Price
  • Order: Change Status, Generate Invoice, Mark as Shipped
  • User: Reset Password, Deactivate User
  • Project Task: Change Status, Assign User
  • Event: Send Invitation, Mark as Completed, Cancel Event

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.sections added to each view entry:

form: {data: {provider: 'object',object: 'account'},sections: [{label: 'Basic Information',columns: '2',fields: ['name','industry','type', ...]},{label: 'Financial',columns: '2',fields: ['annual_revenue','tags']},{label: 'Address',columns: '2',fields: ['billing_address','shipping_address', ...]},{label: 'Additional Details',columns: '1',collapsible: true,fields: ['description', ...]},],},

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.ts reduced from 1418 → 770 lines, now imports and spreads from separate files
  • src/index.ts re-exports all new modules

Note

objectstack compile has a pre-existing re-validation bug on form.sections.columns (Zod transforms string "2" → number 2, then re-validates against the string enum). defineStack() itself accepts the config correctly.

Original prompt

This section details on the original issue you should resolve

<issue_title>Enrich CRM example metadata with full @objectstack/spec capabilities</issue_title>
<issue_description>

🎯 Goal

Maximize the CRM example's use of @objectstack/spec features so that the Console front-end can verify all platform capabilities end-to-end.

📦 What's Being Enhanced

All 8 Object Definitions (account, contact, opportunity, product, order, user, project_task, event):

EnhancementDescription
Object metadatadescription, titleFormat, recordName
Field enrichmentsrequired, searchable, unique, defaultValue, helpText, placeholder, group, readOnly
Field type diversitytext, textarea, richtext, email, url, number, currency, percent, date, datetime, boolean, select (with colors), multi-select, lookup (single/multiple)
list_views2+ list views per object (All, Active/Filtered) with sort/filter
actionsObject-level actions (Send Email, Change Status, etc.) to exercise the Action Engine
form_layoutSections-based form layout organizing fields into logical groups
New fields30+ new fields across all objects (tags, avatar, linkedin, expected_revenue, shipping_address, etc.)

Config & Docs Updates:

  • objectstack.config.ts — Updated seed data, new views leveraging new fields
  • ROADMAP.md — Completion entry added

🔑 Key Principle

  • Only use features actually supported by ObjectSchema.create() and Field.*() from @objectstack/spec/data
  • Keep backward compatibility with existing seed data field names
  • Run tests to ensure nothing breaks

The coding agent is now working on the PR. You'll receive a notification when it's ready for review. 🚀</issue_description>

Comments on the Issue (you are @copilot in this section)

@hotlong 任务没完成 list_views | 2+ list views per object (All, Active/Filtered) with sort/filter -- | -- actions | Object-level actions (Send Email, Change Status, etc.) to exercise the Action Engine form_layout | Sections-based form layout organizing fields into logical groups

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel

vercelBot commented Feb 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
objectuiReadyReadyPreview, CommentFeb 20, 2026 3:01pm
objectui-demoErrorErrorFeb 20, 2026 3:01pm
objectui-storybookReadyReadyPreview, CommentFeb 20, 2026 3:01pm

Request Review

CopilotAIand others added 2 commits February 20, 2026 14:26
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
CopilotAI changed the title [WIP] Enhance CRM example metadata with full @objectstack/spec capabilitiesAdd actions and form_layout sections to CRM example metadataFeb 20, 2026
CopilotAI requested a review from hotlongFebruary 20, 2026 14:31
@hotlong

Copy link
Copy Markdown
Contributor

元数据应该分拆成单独的文件 @copilot

…c/actions/
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>

CopilotAI commented Feb 20, 2026

Copy link
Copy Markdown
ContributorAuthor

元数据应该分拆成单独的文件 @copilot

Done in 9ca0076. Split metadata into per-object files:

  • src/views/ — 8 files (account.view.ts, contact.view.ts, etc.) with listViews + form sections
  • src/actions/ — 8 files (account.actions.ts, contact.actions.ts, etc.) with object-level actions
  • objectstack.config.ts reduced from 1418→770 lines, now imports and spreads from separate files
  • src/index.ts re-exports all new modules

CopilotAI left a comment

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.

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 include form.sections (form layout) and multiple listViews.
  • 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
FileDescription
examples/crm/objectstack.config.tsImports new per-object view/action modules; registers actions and swaps inline views for module references.
examples/crm/src/index.tsRe-exports newly added views/* and actions/* modules for easier consumption.
examples/crm/src/views/account.view.tsAdds form.sections layout and maintains 2 list views for Account.
examples/crm/src/views/contact.view.tsAdds form.sections layout and maintains 2 list views for Contact.
examples/crm/src/views/opportunity.view.tsAdds form.sections layout and multiple list views including kanban pipeline.
examples/crm/src/views/product.view.tsAdds form.sections layout and maintains 2 list views for Product.
examples/crm/src/views/order.view.tsAdds form.sections layout and maintains 2 list views for Order.
examples/crm/src/views/user.view.tsAdds form.sections layout and maintains 2 list views for User.
examples/crm/src/views/event.view.tsAdds form.sections layout and list views including a calendar view.
examples/crm/src/views/project.view.tsAdds form.sections layout and list views including gantt/timeline entries.
examples/crm/src/actions/account.actions.tsIntroduces Account actions with confirm/success metadata and parameter definitions.
examples/crm/src/actions/contact.actions.tsIntroduces Contact actions with confirm/success metadata and parameter definitions.
examples/crm/src/actions/opportunity.actions.tsIntroduces Opportunity actions with confirm/success metadata and parameter definitions.
examples/crm/src/actions/product.actions.tsIntroduces Product actions with parameter definitions.
examples/crm/src/actions/order.actions.tsIntroduces Order actions with confirm/success metadata and parameter definitions.
examples/crm/src/actions/user.actions.tsIntroduces User actions with confirm/success metadata.
examples/crm/src/actions/project.actions.tsIntroduces Project Task actions with parameter definitions.
examples/crm/src/actions/event.actions.tsIntroduces Event actions with confirm/success metadata and parameter definitions.
ROADMAP.mdUpdates the CRM enrichment entry to include actions + form_layout sections.

Comment on lines +3 to +10
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 },

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +23
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',
},

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +22
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',

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +12
locations: ['record_header' as const],
params: [
{ name: 'message', label: 'Optional Message', type: 'textarea' as const },
],
successMessage: 'Invitation sent to all participants',
},

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +12
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',

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +20
{
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',

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
label: 'Timeline',
type: 'timeline' as const,
data: { provider: 'object' as const, object: 'project_task' },
columns: ['name', 'start_date', 'status'],

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
columns: ['name','start_date','status'],
columns: ['name','start_date','status'],
timeline: {
dateField: 'start_date',
endDateField: 'end_date',
},

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +33
{
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',
},

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +37
{
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',
},

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +29
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 }],
},

CopilotAIFeb 20, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Sign up for freeto 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.

Enrich CRM example metadata with full @objectstack/spec capabilities

3 participants

@hotlong