From bade7b2a42245bc3287ac46256c1461ea00a95ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:04:04 +0000 Subject: [PATCH 1/4] Initial plan From 039c031ae9b3b28443d5e46606ec5a7556c301f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:26:49 +0000 Subject: [PATCH 2/4] Add actions and form_layout sections to CRM example metadata Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- examples/crm/objectstack.config.ts | 472 +++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) diff --git a/examples/crm/objectstack.config.ts b/examples/crm/objectstack.config.ts index 944dae24d3..faa21aed6d 100644 --- a/examples/crm/objectstack.config.ts +++ b/examples/crm/objectstack.config.ts @@ -42,6 +42,32 @@ export default defineStack({ sort: [{ field: 'annual_revenue', order: 'desc' }], }, }, + form: { + data: { provider: 'object', object: 'account' }, + sections: [ + { + label: 'Basic Information', + columns: '2', + fields: ['name', 'industry', 'type', 'rating', 'website', 'phone', 'employees', 'owner'], + }, + { + label: 'Financial', + columns: '2', + fields: ['annual_revenue', 'tags'], + }, + { + label: 'Address', + columns: '2', + fields: ['billing_address', 'shipping_address', 'latitude', 'longitude'], + }, + { + label: 'Additional Details', + columns: '1', + collapsible: true, + fields: ['linkedin_url', 'founded_date', 'description', 'created_at'], + }, + ], + }, }, // --- Contact Views --- { @@ -63,6 +89,32 @@ export default defineStack({ filter: ['is_active', '=', true], }, }, + form: { + data: { provider: 'object', object: 'contact' }, + sections: [ + { + label: 'Personal Information', + columns: '2', + fields: ['avatar', 'name', 'email', 'phone', 'title', 'department', 'company'], + }, + { + label: 'Account & Status', + columns: '2', + fields: ['account', 'status', 'priority', 'lead_source', 'is_active', 'do_not_call'], + }, + { + label: 'Address & Social', + columns: '2', + fields: ['address', 'linkedin', 'birthdate', 'latitude', 'longitude'], + }, + { + label: 'Notes', + columns: '1', + collapsible: true, + fields: ['notes'], + }, + ], + }, }, // --- Opportunity Views --- { @@ -95,6 +147,32 @@ export default defineStack({ sort: [{ field: 'close_date', order: 'asc' }], }, }, + form: { + data: { provider: 'object', object: 'opportunity' }, + sections: [ + { + label: 'Deal Information', + columns: '2', + fields: ['name', 'account', 'contacts', 'type', 'lead_source', 'campaign_source'], + }, + { + label: 'Financials & Stage', + columns: '2', + fields: ['amount', 'expected_revenue', 'probability', 'stage', 'forecast_category'], + }, + { + label: 'Timeline', + columns: '2', + fields: ['close_date', 'next_step'], + }, + { + label: 'Description', + columns: '1', + collapsible: true, + fields: ['description'], + }, + ], + }, }, // --- Product Views --- { @@ -116,6 +194,27 @@ export default defineStack({ filter: ['is_active', '=', true], }, }, + form: { + data: { provider: 'object', object: 'product' }, + sections: [ + { + label: 'Product Details', + columns: '2', + fields: ['name', 'sku', 'category', 'manufacturer', 'is_active', 'tags'], + }, + { + label: 'Pricing & Inventory', + columns: '2', + fields: ['price', 'stock', 'weight'], + }, + { + label: 'Media & Description', + columns: '1', + collapsible: true, + fields: ['image', 'description'], + }, + ], + }, }, // --- Order Views --- { @@ -137,6 +236,32 @@ export default defineStack({ filter: ['status', '=', 'pending'], }, }, + form: { + data: { provider: 'object', object: 'order' }, + sections: [ + { + label: 'Order Information', + columns: '2', + fields: ['name', 'customer', 'account', 'order_date', 'status', 'payment_method'], + }, + { + label: 'Financials', + columns: '2', + fields: ['amount', 'discount'], + }, + { + label: 'Shipping', + columns: '2', + fields: ['shipping_address', 'tracking_number'], + }, + { + label: 'Notes', + columns: '1', + collapsible: true, + fields: ['notes'], + }, + ], + }, }, // --- User Views --- { @@ -158,6 +283,27 @@ export default defineStack({ filter: ['active', '=', true], }, }, + form: { + data: { provider: 'object', object: 'user' }, + sections: [ + { + label: 'Profile', + columns: '2', + fields: ['avatar', 'name', 'email', 'username', 'phone'], + }, + { + label: 'Role & Department', + columns: '2', + fields: ['role', 'title', 'department', 'active'], + }, + { + label: 'Bio', + columns: '1', + collapsible: true, + fields: ['bio'], + }, + ], + }, }, // --- Event Views --- { @@ -192,6 +338,32 @@ export default defineStack({ sort: [{ field: 'start', order: 'asc' }], }, }, + form: { + data: { provider: 'object', object: 'event' }, + sections: [ + { + label: 'Event Details', + columns: '2', + fields: ['subject', 'type', 'status', 'location', 'is_all_day', 'is_private'], + }, + { + label: 'Schedule', + columns: '2', + fields: ['start', 'end', 'reminder'], + }, + { + label: 'Participants', + columns: '2', + fields: ['organizer', 'participants'], + }, + { + label: 'Description', + columns: '1', + collapsible: true, + fields: ['description'], + }, + ], + }, }, // --- Project Task Views --- { @@ -230,9 +402,309 @@ export default defineStack({ columns: ['name', 'start_date', 'status'], }, }, + form: { + data: { provider: 'object', object: 'project_task' }, + sections: [ + { + label: 'Task Information', + columns: '2', + fields: ['name', 'status', 'priority', 'color', 'manager', 'assignee'], + }, + { + label: 'Timeline & Progress', + columns: '2', + fields: ['start_date', 'end_date', 'progress', 'estimated_hours', 'actual_hours'], + }, + { + label: 'Details', + columns: '1', + collapsible: true, + fields: ['dependencies', 'description'], + }, + ], + }, }, ], reports: [], + actions: [ + // --- Account Actions --- + { + name: 'account_send_email', + label: 'Send Email', + icon: 'mail', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { name: 'to', label: 'To Email', type: 'email', required: true }, + { name: 'subject', label: 'Subject', type: 'text', required: true }, + { name: 'body', label: 'Message', type: 'textarea' }, + ], + successMessage: 'Email sent successfully', + }, + { + name: 'account_assign_owner', + label: 'Assign Owner', + icon: 'user-plus', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { name: 'owner_id', label: 'New Owner', type: 'lookup', required: true }, + ], + refreshAfter: true, + successMessage: 'Owner assigned successfully', + }, + { + name: 'account_merge', + label: 'Merge Accounts', + icon: 'git-merge', + type: 'api', + locations: ['record_more'], + confirmText: 'Are you sure you want to merge these accounts? This action cannot be undone.', + variant: 'danger', + refreshAfter: true, + }, + // --- Contact Actions --- + { + name: 'contact_send_email', + label: 'Send Email', + icon: 'mail', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { name: 'subject', label: 'Subject', type: 'text', required: true }, + { name: 'body', label: 'Message', type: 'textarea' }, + ], + successMessage: 'Email sent successfully', + }, + { + name: 'contact_convert_to_customer', + label: 'Convert to Customer', + icon: 'user-check', + type: 'api', + locations: ['record_header'], + confirmText: 'Convert this contact to a customer?', + refreshAfter: true, + successMessage: 'Contact converted to customer', + }, + { + name: 'contact_log_call', + label: 'Log a Call', + icon: 'phone', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { name: 'call_subject', label: 'Subject', type: 'text', required: true }, + { name: 'call_notes', label: 'Notes', type: 'textarea' }, + { name: 'call_duration', label: 'Duration (min)', type: 'number' }, + ], + successMessage: 'Call logged successfully', + }, + // --- Opportunity Actions --- + { + name: 'opportunity_change_stage', + label: 'Change Stage', + icon: 'arrow-right-circle', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { + name: 'new_stage', label: 'New Stage', type: 'select', 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', + }, + { + name: 'opportunity_mark_won', + label: 'Mark as Won', + icon: 'trophy', + type: 'api', + locations: ['record_header'], + variant: 'primary', + confirmText: 'Mark this opportunity as Closed Won?', + refreshAfter: true, + successMessage: 'Opportunity marked as won!', + }, + { + name: 'opportunity_mark_lost', + label: 'Mark as Lost', + icon: 'x-circle', + type: 'api', + locations: ['record_more'], + variant: 'danger', + params: [ + { name: 'loss_reason', label: 'Reason for Loss', type: 'text', required: true }, + ], + confirmText: 'Mark this opportunity as Closed Lost?', + refreshAfter: true, + }, + // --- Product Actions --- + { + name: 'product_toggle_active', + label: 'Toggle Active', + icon: 'toggle-left', + type: 'api', + locations: ['record_header', 'list_item'], + refreshAfter: true, + successMessage: 'Product status updated', + }, + { + name: 'product_update_price', + label: 'Update Price', + icon: 'dollar-sign', + type: 'api', + locations: ['record_header'], + params: [ + { name: 'new_price', label: 'New Price', type: 'currency', required: true }, + ], + refreshAfter: true, + successMessage: 'Price updated successfully', + }, + // --- Order Actions --- + { + name: 'order_change_status', + label: 'Change Status', + icon: 'refresh-cw', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { + name: 'new_status', label: 'New Status', type: 'select', 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', + }, + { + name: 'order_generate_invoice', + label: 'Generate Invoice', + icon: 'file-text', + type: 'api', + locations: ['record_header'], + confirmText: 'Generate an invoice for this order?', + successMessage: 'Invoice generated successfully', + }, + { + name: 'order_mark_shipped', + label: 'Mark as Shipped', + icon: 'truck', + type: 'api', + locations: ['record_header'], + params: [ + { name: 'tracking_number', label: 'Tracking Number', type: 'text', required: true }, + { name: 'carrier', label: 'Carrier', type: 'text' }, + ], + refreshAfter: true, + successMessage: 'Order marked as shipped', + }, + // --- User Actions --- + { + name: 'user_reset_password', + label: 'Reset Password', + icon: 'key', + type: 'api', + locations: ['record_header', 'record_more'], + 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', + locations: ['record_more'], + variant: 'danger', + confirmText: 'Are you sure you want to deactivate this user?', + refreshAfter: true, + successMessage: 'User deactivated', + }, + // --- Project Task Actions --- + { + name: 'task_change_status', + label: 'Change Status', + icon: 'circle-check', + type: 'api', + locations: ['record_header', 'list_item'], + params: [ + { + name: 'new_status', label: 'New Status', type: 'select', 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', + locations: ['record_header', 'list_item'], + params: [ + { name: 'assignee_id', label: 'Assignee', type: 'lookup', required: true }, + ], + refreshAfter: true, + successMessage: 'Task assigned successfully', + }, + // --- Event Actions --- + { + name: 'event_send_invitation', + label: 'Send Invitation', + icon: 'send', + type: 'api', + locations: ['record_header'], + params: [ + { name: 'message', label: 'Optional Message', type: 'textarea' }, + ], + successMessage: 'Invitation sent to all participants', + }, + { + name: 'event_mark_completed', + label: 'Mark as Completed', + icon: 'check-circle', + type: 'api', + locations: ['record_header'], + confirmText: 'Mark this event as completed?', + refreshAfter: true, + successMessage: 'Event marked as completed', + }, + { + name: 'event_cancel', + label: 'Cancel Event', + icon: 'x-circle', + type: 'api', + locations: ['record_more'], + variant: 'danger', + params: [ + { name: 'cancel_reason', label: 'Cancellation Reason', type: 'text' }, + { name: 'notify_participants', label: 'Notify Participants', type: 'boolean' }, + ], + confirmText: 'Are you sure you want to cancel this event?', + refreshAfter: true, + successMessage: 'Event cancelled', + }, + ], pages: [ { name: 'crm_help', From 089726539957f9fb47636ca26daeda53d2aa9123 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:29:00 +0000 Subject: [PATCH 3/4] Update ROADMAP.md with actions and form_layout completion entry Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 1aa872a7f3..7feed00363 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -81,7 +81,7 @@ Full adoption of Cloud namespace, contracts/integration/security/studio modules, ### CRM Example Metadata Enrichment ✅ -Enriched all 8 CRM object definitions (`account`, `contact`, `opportunity`, `product`, `order`, `user`, `project_task`, `event`) to exercise the full `@objectstack/spec` feature set. Added `description` to all objects; field enrichments (`required`, `searchable`, `unique`, `defaultValue`, `helpText`, `placeholder`, `readonly`); diverse field types (`richtext`, `phone`, `avatar`, `color`, `multi-select`); 30+ new fields (tags, linkedin, expected_revenue, shipping_address, etc.); 2+ list views per object with sort/filter; select options with colors across all objects; updated seed data leveraging new fields. +Enriched all 8 CRM object definitions (`account`, `contact`, `opportunity`, `product`, `order`, `user`, `project_task`, `event`) to exercise the full `@objectstack/spec` feature set. Added `description` to all objects; field enrichments (`required`, `searchable`, `unique`, `defaultValue`, `readonly`); diverse field types (`richtext`, `phone`, `avatar`, `color`, `multi-select`); 30+ new fields (tags, linkedin, expected_revenue, shipping_address, etc.); 2+ list views per object with sort/filter; select options with colors across all objects; updated seed data leveraging new fields. Added 21 object-level actions across all 8 objects (Send Email, Change Status, Assign Owner, Mark as Won/Lost, Generate Invoice, Reset Password, etc.) with `params`, `confirmText`, `variant`, `locations`, and `refreshAfter`. Added sections-based `form_layout` for all 8 objects organizing fields into logical groups (e.g., Basic Info, Financials, Address, Timeline) with `columns`, `collapsible`, and field grouping. ### Architecture From 9ca00761d2ddeeda6576c512967ca02787e220f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:56:08 +0000 Subject: [PATCH 4/4] Split views and actions into per-object files under src/views/ and src/actions/ Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- examples/crm/objectstack.config.ts | 712 +----------------- examples/crm/src/actions/account.actions.ts | 37 + examples/crm/src/actions/contact.actions.ts | 37 + examples/crm/src/actions/event.actions.ts | 38 + .../crm/src/actions/opportunity.actions.ts | 48 ++ examples/crm/src/actions/order.actions.ts | 46 ++ examples/crm/src/actions/product.actions.ts | 23 + examples/crm/src/actions/project.actions.ts | 34 + examples/crm/src/actions/user.actions.ts | 22 + examples/crm/src/index.ts | 16 + examples/crm/src/views/account.view.ts | 47 ++ examples/crm/src/views/contact.view.ts | 46 ++ examples/crm/src/views/event.view.ts | 59 ++ examples/crm/src/views/opportunity.view.ts | 57 ++ examples/crm/src/views/order.view.ts | 46 ++ examples/crm/src/views/product.view.ts | 41 + examples/crm/src/views/project.view.ts | 58 ++ examples/crm/src/views/user.view.ts | 41 + 18 files changed, 728 insertions(+), 680 deletions(-) create mode 100644 examples/crm/src/actions/account.actions.ts create mode 100644 examples/crm/src/actions/contact.actions.ts create mode 100644 examples/crm/src/actions/event.actions.ts create mode 100644 examples/crm/src/actions/opportunity.actions.ts create mode 100644 examples/crm/src/actions/order.actions.ts create mode 100644 examples/crm/src/actions/product.actions.ts create mode 100644 examples/crm/src/actions/project.actions.ts create mode 100644 examples/crm/src/actions/user.actions.ts create mode 100644 examples/crm/src/views/account.view.ts create mode 100644 examples/crm/src/views/contact.view.ts create mode 100644 examples/crm/src/views/event.view.ts create mode 100644 examples/crm/src/views/opportunity.view.ts create mode 100644 examples/crm/src/views/order.view.ts create mode 100644 examples/crm/src/views/product.view.ts create mode 100644 examples/crm/src/views/project.view.ts create mode 100644 examples/crm/src/views/user.view.ts diff --git a/examples/crm/objectstack.config.ts b/examples/crm/objectstack.config.ts index faa21aed6d..43f01d2d54 100644 --- a/examples/crm/objectstack.config.ts +++ b/examples/crm/objectstack.config.ts @@ -8,6 +8,22 @@ import { OrderObject } from './src/objects/order.object'; import { UserObject } from './src/objects/user.object'; import { ProjectObject } from './src/objects/project.object'; import { EventObject } from './src/objects/event.object'; +import { AccountView } from './src/views/account.view'; +import { ContactView } from './src/views/contact.view'; +import { OpportunityView } from './src/views/opportunity.view'; +import { ProductView } from './src/views/product.view'; +import { OrderView } from './src/views/order.view'; +import { UserView } from './src/views/user.view'; +import { EventView } from './src/views/event.view'; +import { ProjectView } from './src/views/project.view'; +import { AccountActions } from './src/actions/account.actions'; +import { ContactActions } from './src/actions/contact.actions'; +import { OpportunityActions } from './src/actions/opportunity.actions'; +import { ProductActions } from './src/actions/product.actions'; +import { OrderActions } from './src/actions/order.actions'; +import { UserActions } from './src/actions/user.actions'; +import { ProjectActions } from './src/actions/project.actions'; +import { EventActions } from './src/actions/event.actions'; export default defineStack({ objects: [ @@ -21,689 +37,25 @@ export default defineStack({ EventObject ], views: [ - // --- Account Views --- - { - listViews: { - all_accounts: { - name: 'all_accounts', - label: 'All Accounts', - type: 'grid', - data: { provider: 'object', object: 'account' }, - columns: ['name', 'industry', 'type', 'annual_revenue', 'rating', 'owner'], - sort: [{ field: 'name', order: 'asc' }], - }, - active_accounts: { - name: 'active_accounts', - label: 'Active Customers', - type: 'grid', - data: { provider: 'object', object: 'account' }, - columns: ['name', 'industry', 'annual_revenue', 'phone', 'owner'], - filter: ['type', '=', 'Customer'], - sort: [{ field: 'annual_revenue', order: 'desc' }], - }, - }, - form: { - data: { provider: 'object', object: 'account' }, - sections: [ - { - label: 'Basic Information', - columns: '2', - fields: ['name', 'industry', 'type', 'rating', 'website', 'phone', 'employees', 'owner'], - }, - { - label: 'Financial', - columns: '2', - fields: ['annual_revenue', 'tags'], - }, - { - label: 'Address', - columns: '2', - fields: ['billing_address', 'shipping_address', 'latitude', 'longitude'], - }, - { - label: 'Additional Details', - columns: '1', - collapsible: true, - fields: ['linkedin_url', 'founded_date', 'description', 'created_at'], - }, - ], - }, - }, - // --- Contact Views --- - { - listViews: { - all_contacts: { - name: 'all_contacts', - label: 'All Contacts', - type: 'grid', - data: { provider: 'object', object: 'contact' }, - columns: ['name', 'email', 'phone', 'title', 'account', 'status', 'priority'], - sort: [{ field: 'name', order: 'asc' }], - }, - active_contacts: { - name: 'active_contacts', - label: 'Active Contacts', - type: 'grid', - data: { provider: 'object', object: 'contact' }, - columns: ['name', 'email', 'title', 'account', 'status'], - filter: ['is_active', '=', true], - }, - }, - form: { - data: { provider: 'object', object: 'contact' }, - sections: [ - { - label: 'Personal Information', - columns: '2', - fields: ['avatar', 'name', 'email', 'phone', 'title', 'department', 'company'], - }, - { - label: 'Account & Status', - columns: '2', - fields: ['account', 'status', 'priority', 'lead_source', 'is_active', 'do_not_call'], - }, - { - label: 'Address & Social', - columns: '2', - fields: ['address', 'linkedin', 'birthdate', 'latitude', 'longitude'], - }, - { - label: 'Notes', - columns: '1', - collapsible: true, - fields: ['notes'], - }, - ], - }, - }, - // --- Opportunity Views --- - { - listViews: { - all: { - name: 'all', - label: 'All Opportunities', - type: 'grid', - data: { provider: 'object', object: 'opportunity' }, - columns: ['name', 'amount', 'stage', 'close_date', 'probability', 'forecast_category'], - sort: [{ field: 'close_date', order: 'asc' }], - }, - pipeline: { - name: 'pipeline', - label: 'Pipeline', - type: 'kanban', - data: { provider: 'object', object: 'opportunity' }, - columns: ['name', 'amount', 'close_date', 'probability'], - kanban: { - groupByField: 'stage', - columns: ['name', 'amount', 'close_date'], - }, - }, - closing_this_month: { - name: 'closing_this_month', - label: 'Closing This Month', - type: 'grid', - data: { provider: 'object', object: 'opportunity' }, - columns: ['name', 'amount', 'stage', 'close_date', 'probability'], - sort: [{ field: 'close_date', order: 'asc' }], - }, - }, - form: { - data: { provider: 'object', object: 'opportunity' }, - sections: [ - { - label: 'Deal Information', - columns: '2', - fields: ['name', 'account', 'contacts', 'type', 'lead_source', 'campaign_source'], - }, - { - label: 'Financials & Stage', - columns: '2', - fields: ['amount', 'expected_revenue', 'probability', 'stage', 'forecast_category'], - }, - { - label: 'Timeline', - columns: '2', - fields: ['close_date', 'next_step'], - }, - { - label: 'Description', - columns: '1', - collapsible: true, - fields: ['description'], - }, - ], - }, - }, - // --- Product Views --- - { - listViews: { - all_products: { - name: 'all_products', - label: 'All Products', - type: 'grid', - data: { provider: 'object', object: 'product' }, - columns: ['name', 'sku', 'category', 'price', 'stock', 'is_active'], - sort: [{ field: 'name', order: 'asc' }], - }, - active_products: { - name: 'active_products', - label: 'Active Products', - type: 'grid', - data: { provider: 'object', object: 'product' }, - columns: ['name', 'sku', 'category', 'price', 'stock', 'tags'], - filter: ['is_active', '=', true], - }, - }, - form: { - data: { provider: 'object', object: 'product' }, - sections: [ - { - label: 'Product Details', - columns: '2', - fields: ['name', 'sku', 'category', 'manufacturer', 'is_active', 'tags'], - }, - { - label: 'Pricing & Inventory', - columns: '2', - fields: ['price', 'stock', 'weight'], - }, - { - label: 'Media & Description', - columns: '1', - collapsible: true, - fields: ['image', 'description'], - }, - ], - }, - }, - // --- Order Views --- - { - listViews: { - all_orders: { - name: 'all_orders', - label: 'All Orders', - type: 'grid', - data: { provider: 'object', object: 'order' }, - columns: ['name', 'customer', 'amount', 'status', 'order_date', 'payment_method'], - sort: [{ field: 'order_date', order: 'desc' }], - }, - pending_orders: { - name: 'pending_orders', - label: 'Pending Orders', - type: 'grid', - data: { provider: 'object', object: 'order' }, - columns: ['name', 'customer', 'amount', 'order_date'], - filter: ['status', '=', 'pending'], - }, - }, - form: { - data: { provider: 'object', object: 'order' }, - sections: [ - { - label: 'Order Information', - columns: '2', - fields: ['name', 'customer', 'account', 'order_date', 'status', 'payment_method'], - }, - { - label: 'Financials', - columns: '2', - fields: ['amount', 'discount'], - }, - { - label: 'Shipping', - columns: '2', - fields: ['shipping_address', 'tracking_number'], - }, - { - label: 'Notes', - columns: '1', - collapsible: true, - fields: ['notes'], - }, - ], - }, - }, - // --- User Views --- - { - listViews: { - all_users: { - name: 'all_users', - label: 'All Users', - type: 'grid', - data: { provider: 'object', object: 'user' }, - columns: ['name', 'email', 'role', 'department', 'active'], - sort: [{ field: 'name', order: 'asc' }], - }, - active_users: { - name: 'active_users', - label: 'Active Users', - type: 'grid', - data: { provider: 'object', object: 'user' }, - columns: ['name', 'email', 'role', 'title'], - filter: ['active', '=', true], - }, - }, - form: { - data: { provider: 'object', object: 'user' }, - sections: [ - { - label: 'Profile', - columns: '2', - fields: ['avatar', 'name', 'email', 'username', 'phone'], - }, - { - label: 'Role & Department', - columns: '2', - fields: ['role', 'title', 'department', 'active'], - }, - { - label: 'Bio', - columns: '1', - collapsible: true, - fields: ['bio'], - }, - ], - }, - }, - // --- Event Views --- - { - listViews: { - all_events: { - name: 'all_events', - label: 'All Events', - type: 'grid', - data: { provider: 'object', object: 'event' }, - columns: ['subject', 'start', 'end', 'location', 'type', 'status'], - sort: [{ field: 'start', order: 'asc' }], - }, - calendar: { - name: 'calendar', - label: 'Calendar', - type: 'calendar', - data: { provider: 'object', object: 'event' }, - columns: ['subject', 'start', 'end', 'type'], - calendar: { - startDateField: 'start', - endDateField: 'end', - titleField: 'subject', - }, - }, - upcoming_meetings: { - name: 'upcoming_meetings', - label: 'Upcoming Meetings', - type: 'grid', - data: { provider: 'object', object: 'event' }, - columns: ['subject', 'start', 'location', 'organizer'], - filter: ['type', '=', 'meeting'], - sort: [{ field: 'start', order: 'asc' }], - }, - }, - form: { - data: { provider: 'object', object: 'event' }, - sections: [ - { - label: 'Event Details', - columns: '2', - fields: ['subject', 'type', 'status', 'location', 'is_all_day', 'is_private'], - }, - { - label: 'Schedule', - columns: '2', - fields: ['start', 'end', 'reminder'], - }, - { - label: 'Participants', - columns: '2', - fields: ['organizer', 'participants'], - }, - { - label: 'Description', - columns: '1', - collapsible: true, - fields: ['description'], - }, - ], - }, - }, - // --- Project Task Views --- - { - listViews: { - all_tasks: { - name: 'all_tasks', - label: 'All Tasks', - type: 'grid', - data: { provider: 'object', object: 'project_task' }, - columns: ['name', 'status', 'priority', 'start_date', 'end_date', 'progress', 'assignee'], - sort: [{ field: 'start_date', order: 'asc' }], - }, - board: { - name: 'board', - label: 'Board', - type: 'kanban', - data: { provider: 'object', object: 'project_task' }, - columns: ['name', 'priority', 'start_date', 'end_date'], - kanban: { - groupByField: 'status', - columns: ['name', 'priority', 'start_date', 'end_date'], - }, - }, - gantt: { - name: 'gantt', - label: 'Gantt', - type: 'gantt', - data: { provider: 'object', object: 'project_task' }, - columns: ['name', 'start_date', 'end_date', 'progress', 'status'], - }, - timeline: { - name: 'timeline', - label: 'Timeline', - type: 'timeline', - data: { provider: 'object', object: 'project_task' }, - columns: ['name', 'start_date', 'status'], - }, - }, - form: { - data: { provider: 'object', object: 'project_task' }, - sections: [ - { - label: 'Task Information', - columns: '2', - fields: ['name', 'status', 'priority', 'color', 'manager', 'assignee'], - }, - { - label: 'Timeline & Progress', - columns: '2', - fields: ['start_date', 'end_date', 'progress', 'estimated_hours', 'actual_hours'], - }, - { - label: 'Details', - columns: '1', - collapsible: true, - fields: ['dependencies', 'description'], - }, - ], - }, - }, + AccountView, + ContactView, + OpportunityView, + ProductView, + OrderView, + UserView, + EventView, + ProjectView, ], reports: [], actions: [ - // --- Account Actions --- - { - name: 'account_send_email', - label: 'Send Email', - icon: 'mail', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { name: 'to', label: 'To Email', type: 'email', required: true }, - { name: 'subject', label: 'Subject', type: 'text', required: true }, - { name: 'body', label: 'Message', type: 'textarea' }, - ], - successMessage: 'Email sent successfully', - }, - { - name: 'account_assign_owner', - label: 'Assign Owner', - icon: 'user-plus', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { name: 'owner_id', label: 'New Owner', type: 'lookup', required: true }, - ], - refreshAfter: true, - successMessage: 'Owner assigned successfully', - }, - { - name: 'account_merge', - label: 'Merge Accounts', - icon: 'git-merge', - type: 'api', - locations: ['record_more'], - confirmText: 'Are you sure you want to merge these accounts? This action cannot be undone.', - variant: 'danger', - refreshAfter: true, - }, - // --- Contact Actions --- - { - name: 'contact_send_email', - label: 'Send Email', - icon: 'mail', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { name: 'subject', label: 'Subject', type: 'text', required: true }, - { name: 'body', label: 'Message', type: 'textarea' }, - ], - successMessage: 'Email sent successfully', - }, - { - name: 'contact_convert_to_customer', - label: 'Convert to Customer', - icon: 'user-check', - type: 'api', - locations: ['record_header'], - confirmText: 'Convert this contact to a customer?', - refreshAfter: true, - successMessage: 'Contact converted to customer', - }, - { - name: 'contact_log_call', - label: 'Log a Call', - icon: 'phone', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { name: 'call_subject', label: 'Subject', type: 'text', required: true }, - { name: 'call_notes', label: 'Notes', type: 'textarea' }, - { name: 'call_duration', label: 'Duration (min)', type: 'number' }, - ], - successMessage: 'Call logged successfully', - }, - // --- Opportunity Actions --- - { - name: 'opportunity_change_stage', - label: 'Change Stage', - icon: 'arrow-right-circle', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { - name: 'new_stage', label: 'New Stage', type: 'select', 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', - }, - { - name: 'opportunity_mark_won', - label: 'Mark as Won', - icon: 'trophy', - type: 'api', - locations: ['record_header'], - variant: 'primary', - confirmText: 'Mark this opportunity as Closed Won?', - refreshAfter: true, - successMessage: 'Opportunity marked as won!', - }, - { - name: 'opportunity_mark_lost', - label: 'Mark as Lost', - icon: 'x-circle', - type: 'api', - locations: ['record_more'], - variant: 'danger', - params: [ - { name: 'loss_reason', label: 'Reason for Loss', type: 'text', required: true }, - ], - confirmText: 'Mark this opportunity as Closed Lost?', - refreshAfter: true, - }, - // --- Product Actions --- - { - name: 'product_toggle_active', - label: 'Toggle Active', - icon: 'toggle-left', - type: 'api', - locations: ['record_header', 'list_item'], - refreshAfter: true, - successMessage: 'Product status updated', - }, - { - name: 'product_update_price', - label: 'Update Price', - icon: 'dollar-sign', - type: 'api', - locations: ['record_header'], - params: [ - { name: 'new_price', label: 'New Price', type: 'currency', required: true }, - ], - refreshAfter: true, - successMessage: 'Price updated successfully', - }, - // --- Order Actions --- - { - name: 'order_change_status', - label: 'Change Status', - icon: 'refresh-cw', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { - name: 'new_status', label: 'New Status', type: 'select', 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', - }, - { - name: 'order_generate_invoice', - label: 'Generate Invoice', - icon: 'file-text', - type: 'api', - locations: ['record_header'], - confirmText: 'Generate an invoice for this order?', - successMessage: 'Invoice generated successfully', - }, - { - name: 'order_mark_shipped', - label: 'Mark as Shipped', - icon: 'truck', - type: 'api', - locations: ['record_header'], - params: [ - { name: 'tracking_number', label: 'Tracking Number', type: 'text', required: true }, - { name: 'carrier', label: 'Carrier', type: 'text' }, - ], - refreshAfter: true, - successMessage: 'Order marked as shipped', - }, - // --- User Actions --- - { - name: 'user_reset_password', - label: 'Reset Password', - icon: 'key', - type: 'api', - locations: ['record_header', 'record_more'], - 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', - locations: ['record_more'], - variant: 'danger', - confirmText: 'Are you sure you want to deactivate this user?', - refreshAfter: true, - successMessage: 'User deactivated', - }, - // --- Project Task Actions --- - { - name: 'task_change_status', - label: 'Change Status', - icon: 'circle-check', - type: 'api', - locations: ['record_header', 'list_item'], - params: [ - { - name: 'new_status', label: 'New Status', type: 'select', 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', - locations: ['record_header', 'list_item'], - params: [ - { name: 'assignee_id', label: 'Assignee', type: 'lookup', required: true }, - ], - refreshAfter: true, - successMessage: 'Task assigned successfully', - }, - // --- Event Actions --- - { - name: 'event_send_invitation', - label: 'Send Invitation', - icon: 'send', - type: 'api', - locations: ['record_header'], - params: [ - { name: 'message', label: 'Optional Message', type: 'textarea' }, - ], - successMessage: 'Invitation sent to all participants', - }, - { - name: 'event_mark_completed', - label: 'Mark as Completed', - icon: 'check-circle', - type: 'api', - locations: ['record_header'], - confirmText: 'Mark this event as completed?', - refreshAfter: true, - successMessage: 'Event marked as completed', - }, - { - name: 'event_cancel', - label: 'Cancel Event', - icon: 'x-circle', - type: 'api', - locations: ['record_more'], - variant: 'danger', - params: [ - { name: 'cancel_reason', label: 'Cancellation Reason', type: 'text' }, - { name: 'notify_participants', label: 'Notify Participants', type: 'boolean' }, - ], - confirmText: 'Are you sure you want to cancel this event?', - refreshAfter: true, - successMessage: 'Event cancelled', - }, + ...AccountActions, + ...ContactActions, + ...OpportunityActions, + ...ProductActions, + ...OrderActions, + ...UserActions, + ...ProjectActions, + ...EventActions, ], pages: [ { diff --git a/examples/crm/src/actions/account.actions.ts b/examples/crm/src/actions/account.actions.ts new file mode 100644 index 0000000000..07051920c1 --- /dev/null +++ b/examples/crm/src/actions/account.actions.ts @@ -0,0 +1,37 @@ +export const AccountActions = [ + { + 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 }, + { name: 'body', label: 'Message', type: 'textarea' as const }, + ], + successMessage: 'Email sent successfully', + }, + { + name: 'account_assign_owner', + label: 'Assign Owner', + icon: 'user-plus', + type: 'api' as const, + locations: ['record_header' as const, 'list_item' as const], + params: [ + { name: 'owner_id', label: 'New Owner', type: 'lookup' as const, required: true }, + ], + refreshAfter: true, + successMessage: 'Owner assigned successfully', + }, + { + name: 'account_merge', + label: 'Merge Accounts', + icon: 'git-merge', + type: 'api' as const, + locations: ['record_more' as const], + confirmText: 'Are you sure you want to merge these accounts? This action cannot be undone.', + variant: 'danger' as const, + refreshAfter: true, + }, +]; diff --git a/examples/crm/src/actions/contact.actions.ts b/examples/crm/src/actions/contact.actions.ts new file mode 100644 index 0000000000..1a0a44926f --- /dev/null +++ b/examples/crm/src/actions/contact.actions.ts @@ -0,0 +1,37 @@ +export const ContactActions = [ + { + name: 'contact_send_email', + label: 'Send Email', + icon: 'mail', + type: 'api' as const, + 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', + }, + { + name: 'contact_convert_to_customer', + label: 'Convert to Customer', + icon: 'user-check', + type: 'api' as const, + locations: ['record_header' as const], + confirmText: 'Convert this contact to a customer?', + refreshAfter: true, + successMessage: 'Contact converted to customer', + }, + { + name: 'contact_log_call', + label: 'Log a Call', + icon: 'phone', + type: 'api' as const, + locations: ['record_header' as const, 'list_item' as const], + params: [ + { name: 'call_subject', label: 'Subject', type: 'text' as const, required: true }, + { name: 'call_notes', label: 'Notes', type: 'textarea' as const }, + { name: 'call_duration', label: 'Duration (min)', type: 'number' as const }, + ], + successMessage: 'Call logged successfully', + }, +]; diff --git a/examples/crm/src/actions/event.actions.ts b/examples/crm/src/actions/event.actions.ts new file mode 100644 index 0000000000..d2632d89d5 --- /dev/null +++ b/examples/crm/src/actions/event.actions.ts @@ -0,0 +1,38 @@ +export const EventActions = [ + { + 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', + }, +]; diff --git a/examples/crm/src/actions/opportunity.actions.ts b/examples/crm/src/actions/opportunity.actions.ts new file mode 100644 index 0000000000..3d04da3bde --- /dev/null +++ b/examples/crm/src/actions/opportunity.actions.ts @@ -0,0 +1,48 @@ +export const OpportunityActions = [ + { + name: 'opportunity_change_stage', + label: 'Change Stage', + icon: 'arrow-right-circle', + type: 'api' as const, + 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', + }, + { + name: 'opportunity_mark_won', + label: 'Mark as Won', + icon: 'trophy', + type: 'api' as const, + locations: ['record_header' as const], + variant: 'primary' as const, + confirmText: 'Mark this opportunity as Closed Won?', + refreshAfter: true, + successMessage: 'Opportunity marked as won!', + }, + { + name: 'opportunity_mark_lost', + label: 'Mark as Lost', + icon: 'x-circle', + type: 'api' as const, + locations: ['record_more' as const], + variant: 'danger' as const, + params: [ + { name: 'loss_reason', label: 'Reason for Loss', type: 'text' as const, required: true }, + ], + confirmText: 'Mark this opportunity as Closed Lost?', + refreshAfter: true, + }, +]; diff --git a/examples/crm/src/actions/order.actions.ts b/examples/crm/src/actions/order.actions.ts new file mode 100644 index 0000000000..432304632c --- /dev/null +++ b/examples/crm/src/actions/order.actions.ts @@ -0,0 +1,46 @@ +export const OrderActions = [ + { + name: 'order_change_status', + label: 'Change Status', + icon: 'refresh-cw', + 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: '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', + }, + { + name: 'order_generate_invoice', + label: 'Generate Invoice', + icon: 'file-text', + type: 'api' as const, + locations: ['record_header' as const], + confirmText: 'Generate an invoice for this order?', + successMessage: 'Invoice generated successfully', + }, + { + name: 'order_mark_shipped', + label: 'Mark as Shipped', + icon: 'truck', + type: 'api' as const, + locations: ['record_header' as const], + params: [ + { name: 'tracking_number', label: 'Tracking Number', type: 'text' as const, required: true }, + { name: 'carrier', label: 'Carrier', type: 'text' as const }, + ], + refreshAfter: true, + successMessage: 'Order marked as shipped', + }, +]; diff --git a/examples/crm/src/actions/product.actions.ts b/examples/crm/src/actions/product.actions.ts new file mode 100644 index 0000000000..c5eae1e556 --- /dev/null +++ b/examples/crm/src/actions/product.actions.ts @@ -0,0 +1,23 @@ +export const ProductActions = [ + { + name: 'product_toggle_active', + label: 'Toggle Active', + icon: 'toggle-left', + type: 'api' as const, + locations: ['record_header' as const, 'list_item' as const], + refreshAfter: true, + successMessage: 'Product status updated', + }, + { + name: 'product_update_price', + label: 'Update Price', + icon: 'dollar-sign', + type: 'api' as const, + locations: ['record_header' as const], + params: [ + { name: 'new_price', label: 'New Price', type: 'currency' as const, required: true }, + ], + refreshAfter: true, + successMessage: 'Price updated successfully', + }, +]; diff --git a/examples/crm/src/actions/project.actions.ts b/examples/crm/src/actions/project.actions.ts new file mode 100644 index 0000000000..2cf8621b18 --- /dev/null +++ b/examples/crm/src/actions/project.actions.ts @@ -0,0 +1,34 @@ +export const ProjectActions = [ + { + 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', + }, +]; diff --git a/examples/crm/src/actions/user.actions.ts b/examples/crm/src/actions/user.actions.ts new file mode 100644 index 0000000000..fac610f92a --- /dev/null +++ b/examples/crm/src/actions/user.actions.ts @@ -0,0 +1,22 @@ +export const UserActions = [ + { + 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', + }, +]; diff --git a/examples/crm/src/index.ts b/examples/crm/src/index.ts index 8aba64c8b2..0bf19a7d69 100644 --- a/examples/crm/src/index.ts +++ b/examples/crm/src/index.ts @@ -6,4 +6,20 @@ export * from './objects/order.object'; export * from './objects/user.object'; export * from './objects/project.object'; export * from './objects/event.object'; +export * from './views/account.view'; +export * from './views/contact.view'; +export * from './views/opportunity.view'; +export * from './views/product.view'; +export * from './views/order.view'; +export * from './views/user.view'; +export * from './views/event.view'; +export * from './views/project.view'; +export * from './actions/account.actions'; +export * from './actions/contact.actions'; +export * from './actions/opportunity.actions'; +export * from './actions/product.actions'; +export * from './actions/order.actions'; +export * from './actions/user.actions'; +export * from './actions/project.actions'; +export * from './actions/event.actions'; export { default as config } from '../objectstack.config'; diff --git a/examples/crm/src/views/account.view.ts b/examples/crm/src/views/account.view.ts new file mode 100644 index 0000000000..a4098c50a5 --- /dev/null +++ b/examples/crm/src/views/account.view.ts @@ -0,0 +1,47 @@ +export const AccountView = { + listViews: { + all_accounts: { + name: 'all_accounts', + label: 'All Accounts', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'account' }, + columns: ['name', 'industry', 'type', 'annual_revenue', 'rating', 'owner'], + sort: [{ field: 'name', order: 'asc' as const }], + }, + active_accounts: { + name: 'active_accounts', + label: 'Active Customers', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'account' }, + columns: ['name', 'industry', 'annual_revenue', 'phone', 'owner'], + filter: ['type', '=', 'Customer'], + sort: [{ field: 'annual_revenue', order: 'desc' as const }], + }, + }, + form: { + data: { provider: 'object' as const, object: 'account' }, + sections: [ + { + label: 'Basic Information', + columns: '2' as const, + fields: ['name', 'industry', 'type', 'rating', 'website', 'phone', 'employees', 'owner'], + }, + { + label: 'Financial', + columns: '2' as const, + fields: ['annual_revenue', 'tags'], + }, + { + label: 'Address', + columns: '2' as const, + fields: ['billing_address', 'shipping_address', 'latitude', 'longitude'], + }, + { + label: 'Additional Details', + columns: '1' as const, + collapsible: true, + fields: ['linkedin_url', 'founded_date', 'description', 'created_at'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/contact.view.ts b/examples/crm/src/views/contact.view.ts new file mode 100644 index 0000000000..ca21e52fd9 --- /dev/null +++ b/examples/crm/src/views/contact.view.ts @@ -0,0 +1,46 @@ +export const ContactView = { + listViews: { + all_contacts: { + name: 'all_contacts', + label: 'All Contacts', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'contact' }, + columns: ['name', 'email', 'phone', 'title', 'account', 'status', 'priority'], + sort: [{ field: 'name', order: 'asc' as const }], + }, + active_contacts: { + name: 'active_contacts', + label: 'Active Contacts', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'contact' }, + columns: ['name', 'email', 'title', 'account', 'status'], + filter: ['is_active', '=', true], + }, + }, + form: { + data: { provider: 'object' as const, object: 'contact' }, + sections: [ + { + label: 'Personal Information', + columns: '2' as const, + fields: ['avatar', 'name', 'email', 'phone', 'title', 'department', 'company'], + }, + { + label: 'Account & Status', + columns: '2' as const, + fields: ['account', 'status', 'priority', 'lead_source', 'is_active', 'do_not_call'], + }, + { + label: 'Address & Social', + columns: '2' as const, + fields: ['address', 'linkedin', 'birthdate', 'latitude', 'longitude'], + }, + { + label: 'Notes', + columns: '1' as const, + collapsible: true, + fields: ['notes'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/event.view.ts b/examples/crm/src/views/event.view.ts new file mode 100644 index 0000000000..89821100c4 --- /dev/null +++ b/examples/crm/src/views/event.view.ts @@ -0,0 +1,59 @@ +export const EventView = { + listViews: { + all_events: { + name: 'all_events', + label: 'All Events', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'event' }, + columns: ['subject', 'start', 'end', 'location', 'type', 'status'], + sort: [{ field: 'start', order: 'asc' as const }], + }, + calendar: { + name: 'calendar', + label: 'Calendar', + type: 'calendar' as const, + data: { provider: 'object' as const, object: 'event' }, + columns: ['subject', 'start', 'end', 'type'], + calendar: { + startDateField: 'start', + endDateField: 'end', + titleField: 'subject', + }, + }, + upcoming_meetings: { + name: 'upcoming_meetings', + label: 'Upcoming Meetings', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'event' }, + columns: ['subject', 'start', 'location', 'organizer'], + filter: ['type', '=', 'meeting'], + sort: [{ field: 'start', order: 'asc' as const }], + }, + }, + form: { + data: { provider: 'object' as const, object: 'event' }, + sections: [ + { + label: 'Event Details', + columns: '2' as const, + fields: ['subject', 'type', 'status', 'location', 'is_all_day', 'is_private'], + }, + { + label: 'Schedule', + columns: '2' as const, + fields: ['start', 'end', 'reminder'], + }, + { + label: 'Participants', + columns: '2' as const, + fields: ['organizer', 'participants'], + }, + { + label: 'Description', + columns: '1' as const, + collapsible: true, + fields: ['description'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/opportunity.view.ts b/examples/crm/src/views/opportunity.view.ts new file mode 100644 index 0000000000..48f2baee4c --- /dev/null +++ b/examples/crm/src/views/opportunity.view.ts @@ -0,0 +1,57 @@ +export const OpportunityView = { + listViews: { + all: { + name: 'all', + label: 'All Opportunities', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'opportunity' }, + columns: ['name', 'amount', 'stage', 'close_date', 'probability', 'forecast_category'], + sort: [{ field: 'close_date', order: 'asc' as const }], + }, + pipeline: { + name: 'pipeline', + label: 'Pipeline', + type: 'kanban' as const, + data: { provider: 'object' as const, object: 'opportunity' }, + columns: ['name', 'amount', 'close_date', 'probability'], + kanban: { + groupByField: 'stage', + columns: ['name', 'amount', 'close_date'], + }, + }, + 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 }], + }, + }, + form: { + data: { provider: 'object' as const, object: 'opportunity' }, + sections: [ + { + label: 'Deal Information', + columns: '2' as const, + fields: ['name', 'account', 'contacts', 'type', 'lead_source', 'campaign_source'], + }, + { + label: 'Financials & Stage', + columns: '2' as const, + fields: ['amount', 'expected_revenue', 'probability', 'stage', 'forecast_category'], + }, + { + label: 'Timeline', + columns: '2' as const, + fields: ['close_date', 'next_step'], + }, + { + label: 'Description', + columns: '1' as const, + collapsible: true, + fields: ['description'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/order.view.ts b/examples/crm/src/views/order.view.ts new file mode 100644 index 0000000000..35561a17c8 --- /dev/null +++ b/examples/crm/src/views/order.view.ts @@ -0,0 +1,46 @@ +export const OrderView = { + listViews: { + all_orders: { + name: 'all_orders', + label: 'All Orders', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'order' }, + columns: ['name', 'customer', 'amount', 'status', 'order_date', 'payment_method'], + sort: [{ field: 'order_date', order: 'desc' as const }], + }, + pending_orders: { + name: 'pending_orders', + label: 'Pending Orders', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'order' }, + columns: ['name', 'customer', 'amount', 'order_date'], + filter: ['status', '=', 'pending'], + }, + }, + form: { + data: { provider: 'object' as const, object: 'order' }, + sections: [ + { + label: 'Order Information', + columns: '2' as const, + fields: ['name', 'customer', 'account', 'order_date', 'status', 'payment_method'], + }, + { + label: 'Financials', + columns: '2' as const, + fields: ['amount', 'discount'], + }, + { + label: 'Shipping', + columns: '2' as const, + fields: ['shipping_address', 'tracking_number'], + }, + { + label: 'Notes', + columns: '1' as const, + collapsible: true, + fields: ['notes'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/product.view.ts b/examples/crm/src/views/product.view.ts new file mode 100644 index 0000000000..60d649d237 --- /dev/null +++ b/examples/crm/src/views/product.view.ts @@ -0,0 +1,41 @@ +export const ProductView = { + listViews: { + all_products: { + name: 'all_products', + label: 'All Products', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'product' }, + columns: ['name', 'sku', 'category', 'price', 'stock', 'is_active'], + sort: [{ field: 'name', order: 'asc' as const }], + }, + active_products: { + name: 'active_products', + label: 'Active Products', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'product' }, + columns: ['name', 'sku', 'category', 'price', 'stock', 'tags'], + filter: ['is_active', '=', true], + }, + }, + form: { + data: { provider: 'object' as const, object: 'product' }, + sections: [ + { + label: 'Product Details', + columns: '2' as const, + fields: ['name', 'sku', 'category', 'manufacturer', 'is_active', 'tags'], + }, + { + label: 'Pricing & Inventory', + columns: '2' as const, + fields: ['price', 'stock', 'weight'], + }, + { + label: 'Media & Description', + columns: '1' as const, + collapsible: true, + fields: ['image', 'description'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/project.view.ts b/examples/crm/src/views/project.view.ts new file mode 100644 index 0000000000..2de660b690 --- /dev/null +++ b/examples/crm/src/views/project.view.ts @@ -0,0 +1,58 @@ +export const ProjectView = { + listViews: { + all_tasks: { + name: 'all_tasks', + label: 'All Tasks', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'project_task' }, + columns: ['name', 'status', 'priority', 'start_date', 'end_date', 'progress', 'assignee'], + sort: [{ field: 'start_date', order: 'asc' as const }], + }, + board: { + name: 'board', + label: 'Board', + type: 'kanban' as const, + data: { provider: 'object' as const, object: 'project_task' }, + columns: ['name', 'priority', 'start_date', 'end_date'], + kanban: { + groupByField: 'status', + columns: ['name', 'priority', 'start_date', 'end_date'], + }, + }, + gantt: { + name: 'gantt', + label: 'Gantt', + type: 'gantt' as const, + data: { provider: 'object' as const, object: 'project_task' }, + columns: ['name', 'start_date', 'end_date', 'progress', 'status'], + }, + timeline: { + name: 'timeline', + label: 'Timeline', + type: 'timeline' as const, + data: { provider: 'object' as const, object: 'project_task' }, + columns: ['name', 'start_date', 'status'], + }, + }, + form: { + data: { provider: 'object' as const, object: 'project_task' }, + sections: [ + { + label: 'Task Information', + columns: '2' as const, + fields: ['name', 'status', 'priority', 'color', 'manager', 'assignee'], + }, + { + label: 'Timeline & Progress', + columns: '2' as const, + fields: ['start_date', 'end_date', 'progress', 'estimated_hours', 'actual_hours'], + }, + { + label: 'Details', + columns: '1' as const, + collapsible: true, + fields: ['dependencies', 'description'], + }, + ], + }, +}; diff --git a/examples/crm/src/views/user.view.ts b/examples/crm/src/views/user.view.ts new file mode 100644 index 0000000000..18a4598245 --- /dev/null +++ b/examples/crm/src/views/user.view.ts @@ -0,0 +1,41 @@ +export const UserView = { + listViews: { + all_users: { + name: 'all_users', + label: 'All Users', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'user' }, + columns: ['name', 'email', 'role', 'department', 'active'], + sort: [{ field: 'name', order: 'asc' as const }], + }, + active_users: { + name: 'active_users', + label: 'Active Users', + type: 'grid' as const, + data: { provider: 'object' as const, object: 'user' }, + columns: ['name', 'email', 'role', 'title'], + filter: ['active', '=', true], + }, + }, + form: { + data: { provider: 'object' as const, object: 'user' }, + sections: [ + { + label: 'Profile', + columns: '2' as const, + fields: ['avatar', 'name', 'email', 'username', 'phone'], + }, + { + label: 'Role & Department', + columns: '2' as const, + fields: ['role', 'title', 'department', 'active'], + }, + { + label: 'Bio', + columns: '1' as const, + collapsible: true, + fields: ['bio'], + }, + ], + }, +};