diff --git a/ROADMAP.md b/ROADMAP.md index 180078a0c..88e617561 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -73,6 +73,10 @@ All 11 plugin views (Grid, Kanban, Form, Dashboard, Calendar, Timeline, List, De Full adoption of Cloud namespace, contracts/integration/security/studio modules, v3.0.0 PaginatedResult API, ObjectStackAdapter metadata API, 17 compatibility tests, 70+ spec UI types re-exported. +### 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. + ### Architecture ``` diff --git a/apps/console/src/__tests__/ObjectForm.test.tsx b/apps/console/src/__tests__/ObjectForm.test.tsx index d47db9269..4af03de1e 100644 --- a/apps/console/src/__tests__/ObjectForm.test.tsx +++ b/apps/console/src/__tests__/ObjectForm.test.tsx @@ -211,7 +211,7 @@ describe('ObjectForm with MSW Integration', () => { const createdContact = onSuccess.mock.calls[0][0]; // Check default values from schema - expect(createdContact.priority).toBe('Medium'); + expect(createdContact.priority).toBe('medium'); expect(createdContact.is_active).toBe(true); }); }); @@ -395,8 +395,8 @@ describe('ObjectForm with MSW Integration', () => { expect(screen.getByLabelText(/^Name/i)).toBeInTheDocument(); }); - const textarea = screen.getByLabelText(/Notes/i) as HTMLTextAreaElement; - expect(textarea.tagName).toBe('TEXTAREA'); + const textarea = screen.getByLabelText(/Notes/i) as HTMLElement; + expect(['TEXTAREA', 'INPUT', 'DIV'].includes(textarea.tagName)).toBe(true); }); it('should render phone input with tel type', async () => { @@ -417,7 +417,7 @@ describe('ObjectForm with MSW Integration', () => { }); const phoneInput = screen.getByLabelText(/^Phone/i) as HTMLInputElement; - expect(phoneInput.type).toBe('text'); + expect(phoneInput.type).toBe('tel'); }); }); diff --git a/apps/console/src/__tests__/ObjectGrid.test.tsx b/apps/console/src/__tests__/ObjectGrid.test.tsx index afd017ae5..e0c437fc2 100644 --- a/apps/console/src/__tests__/ObjectGrid.test.tsx +++ b/apps/console/src/__tests__/ObjectGrid.test.tsx @@ -68,7 +68,7 @@ describe('ObjectGrid MSW Integration', () => { // Check that all specified columns are rendered expect(screen.getAllByText('415-555-1001')[0]).toBeInTheDocument(); // expect(screen.getByText('ObjectStack HQ')).toBeInTheDocument(); - expect(screen.getAllByText('Active')[0]).toBeInTheDocument(); + expect(screen.getAllByText(/^[Aa]ctive$/)[0]).toBeInTheDocument(); }); it('should handle empty data gracefully', async () => { diff --git a/examples/crm/objectstack.config.ts b/examples/crm/objectstack.config.ts index afbccbc92..944dae24d 100644 --- a/examples/crm/objectstack.config.ts +++ b/examples/crm/objectstack.config.ts @@ -21,6 +21,50 @@ 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' }], + }, + }, + }, + // --- 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], + }, + }, + }, + // --- Opportunity Views --- { listViews: { all: { @@ -28,7 +72,8 @@ export default defineStack({ label: 'All Opportunities', type: 'grid', data: { provider: 'object', object: 'opportunity' }, - columns: ['name', 'amount', 'stage', 'close_date', 'probability'], + columns: ['name', 'amount', 'stage', 'close_date', 'probability', 'forecast_category'], + sort: [{ field: 'close_date', order: 'asc' }], }, pipeline: { name: 'pipeline', @@ -41,8 +86,80 @@ export default defineStack({ 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' }], + }, + }, + }, + // --- 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], + }, }, }, + // --- 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'], + }, + }, + }, + // --- 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], + }, + }, + }, + // --- Event Views --- { listViews: { all_events: { @@ -50,7 +167,8 @@ export default defineStack({ label: 'All Events', type: 'grid', data: { provider: 'object', object: 'event' }, - columns: ['subject', 'start', 'end', 'location', 'type'], + columns: ['subject', 'start', 'end', 'location', 'type', 'status'], + sort: [{ field: 'start', order: 'asc' }], }, calendar: { name: 'calendar', @@ -64,8 +182,18 @@ export default defineStack({ 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' }], + }, }, }, + // --- Project Task Views --- { listViews: { all_tasks: { @@ -73,7 +201,8 @@ export default defineStack({ label: 'All Tasks', type: 'grid', data: { provider: 'object', object: 'project_task' }, - columns: ['name', 'status', 'priority', 'start_date', 'end_date', 'progress'], + columns: ['name', 'status', 'priority', 'start_date', 'end_date', 'progress', 'assignee'], + sort: [{ field: 'start_date', order: 'asc' }], }, board: { name: 'board', @@ -398,11 +527,16 @@ export default defineStack({ industry: "Technology", type: "Partner", employees: 120, - billing_address: "44 Tehama St, San Francisco, CA 94105", + billing_address: "44 Tehama St, San Francisco, CA 94105", + shipping_address: "44 Tehama St, San Francisco, CA 94105", latitude: 37.7879, longitude: -122.3961, - website: "https://objectstack.com", + website: "https://objectstack.com", phone: "415-555-0101", + linkedin_url: "https://linkedin.com/company/objectstack", + tags: ['enterprise', 'strategic'], + rating: "hot", + founded_date: new Date("2020-06-01"), owner: "1", created_at: new Date("2023-01-15") }, @@ -412,11 +546,15 @@ export default defineStack({ industry: "Technology", type: "Customer", employees: 35000, - billing_address: "415 Mission St, San Francisco, CA 94105", + billing_address: "415 Mission St, San Francisco, CA 94105", latitude: 37.7897, longitude: -122.3972, - website: "https://salesforce.com", + website: "https://salesforce.com", phone: "415-555-0102", + linkedin_url: "https://linkedin.com/company/salesforce", + tags: ['enterprise'], + rating: "hot", + annual_revenue: 26000000, owner: "1", created_at: new Date("2023-02-20") }, @@ -426,11 +564,14 @@ export default defineStack({ industry: "Finance", type: "Customer", employees: 5000, - billing_address: "100 Wall St, New York, NY 10005", + billing_address: "100 Wall St, New York, NY 10005", latitude: 40.7056, longitude: -74.0084, - website: "https://globalfin.example.com", + website: "https://globalfin.example.com", phone: "212-555-0103", + tags: ['enterprise'], + rating: "warm", + annual_revenue: 8500000, owner: "2", created_at: new Date("2023-03-10") }, @@ -440,11 +581,13 @@ export default defineStack({ industry: "Services", type: "Partner", employees: 250, - billing_address: "10 Downing St, London, UK", + billing_address: "10 Downing St, London, UK", latitude: 51.5034, longitude: -0.1276, - website: "https://lcg.example.co.uk", + website: "https://lcg.example.co.uk", phone: "+44-555-0104", + tags: ['smb'], + rating: "warm", owner: "2", created_at: new Date("2023-04-05") }, @@ -454,11 +597,13 @@ export default defineStack({ industry: "Retail", type: "Vendor", employees: 80, - billing_address: "Shibuya Crossing, Tokyo, Japan", + billing_address: "Shibuya Crossing, Tokyo, Japan", latitude: 35.6595, longitude: 139.7004, - website: "https://tokyoshop.example.jp", + website: "https://tokyoshop.example.jp", phone: "+81-555-0105", + tags: ['startup'], + rating: "cold", owner: "1", created_at: new Date("2023-05-20") }, @@ -468,11 +613,15 @@ export default defineStack({ industry: "Manufacturing", type: "Customer", employees: 1200, - billing_address: "Berlin, Germany", + billing_address: "Berlin, Germany", + shipping_address: "Industriepark 12, Berlin, Germany", latitude: 52.5200, longitude: 13.4050, - website: "https://berlinauto.example.de", + website: "https://berlinauto.example.de", phone: "+49-555-0106", + tags: ['enterprise', 'strategic'], + rating: "hot", + annual_revenue: 42000000, owner: "1", created_at: new Date("2023-06-15") }, @@ -482,11 +631,14 @@ export default defineStack({ industry: "Retail", type: "Customer", employees: 450, - billing_address: "Champs-Élysées, Paris, France", + billing_address: "Champs-Élysées, Paris, France", latitude: 48.8698, longitude: 2.3075, - website: "https://mode.example.fr", + website: "https://mode.example.fr", phone: "+33-555-0107", + tags: ['smb'], + rating: "warm", + annual_revenue: 3200000, owner: "2", created_at: new Date("2023-07-01") } @@ -504,8 +656,10 @@ export default defineStack({ title: "VP Sales", department: "Sales", account: "1", - status: "Active", - priority: "High", + status: "active", + priority: "high", + lead_source: "Partner", + linkedin: "https://linkedin.com/in/alicejohnson", birthdate: new Date("1985-04-12"), latitude: 37.7879, longitude: -122.3961, @@ -519,8 +673,10 @@ export default defineStack({ title: "CTO", department: "Engineering", account: "2", - status: "Active", - priority: "High", + status: "active", + priority: "high", + lead_source: "Referral", + linkedin: "https://linkedin.com/in/bobsmith", birthdate: new Date("1980-08-23"), latitude: 37.7897, longitude: -122.3972, @@ -534,8 +690,9 @@ export default defineStack({ title: "Procurement Manager", department: "Purchasing", account: "3", - status: "Customer", - priority: "Medium", + status: "customer", + priority: "medium", + lead_source: "Web", birthdate: new Date("1990-01-15"), latitude: 40.7056, longitude: -74.0084, @@ -549,12 +706,14 @@ export default defineStack({ title: "Director", department: "Management", account: "4", - status: "Partner", - priority: "High", + status: "active", + priority: "high", + lead_source: "Referral", birthdate: new Date("1988-06-05"), latitude: 51.5034, longitude: -0.1276, - address: "London, UK" + address: "London, UK", + do_not_call: true }, { _id: "5", @@ -564,8 +723,9 @@ export default defineStack({ title: "Head of Operations", department: "Operations", account: "6", - status: "Lead", - priority: "High", + status: "lead", + priority: "high", + lead_source: "Web", birthdate: new Date("1975-11-30"), latitude: 52.5200, longitude: 13.4050, @@ -579,8 +739,9 @@ export default defineStack({ title: "Creative Director", department: "Design", account: "7", - status: "Customer", - priority: "Low", + status: "customer", + priority: "low", + lead_source: "Trade Show", birthdate: new Date("1992-03-18"), latitude: 48.8698, longitude: 2.3075, @@ -594,8 +755,9 @@ export default defineStack({ title: "Senior Developer", department: "Engineering", account: "2", - status: "Active", - priority: "Low", + status: "active", + priority: "low", + lead_source: "Phone", birthdate: new Date("1995-12-12"), latitude: 37.7897, longitude: -122.3972, @@ -611,10 +773,12 @@ export default defineStack({ _id: "101", name: "ObjectStack Enterprise License", amount: 150000, - stage: "Closed Won", + expected_revenue: 150000, + stage: "closed_won", + forecast_category: "commit", close_date: new Date("2024-01-15"), - account_id: "2", - contact_ids: ["2", "7"], + account: "2", + contacts: ["2", "7"], probability: 100, type: "New Business", lead_source: "Partner", @@ -624,53 +788,62 @@ export default defineStack({ { _id: "102", name: "Global Fin Q1 Upsell", - amount: 45000, - stage: "Negotiation", + amount: 45000, + expected_revenue: 36000, + stage: "negotiation", + forecast_category: "best_case", close_date: new Date("2024-03-30"), - account_id: "3", - contact_ids: ["3"], + account: "3", + contacts: ["3"], probability: 80, type: "Upgrade", - lead_source: "Existing Business", + lead_source: "Web", next_step: "Review Contract", description: "Adding 50 more seats to the existing contract." }, { _id: "103", name: "London Annual Renewal", - amount: 85000, - stage: "Proposal", + amount: 85000, + expected_revenue: 51000, + stage: "proposal", + forecast_category: "pipeline", close_date: new Date("2024-05-15"), - account_id: "4", - contact_ids: ["4"], + account: "4", + contacts: ["4"], probability: 60, type: "Renewal", - lead_source: "Existing Business", + lead_source: "Referral", next_step: "Send Quote", description: "Annual renewal for continuous integration services." }, { _id: "104", name: "Berlin Automation Project", - amount: 250000, - stage: "Prospecting", + amount: 250000, + expected_revenue: 50000, + stage: "prospecting", + forecast_category: "pipeline", close_date: new Date("2024-09-01"), - account_id: "6", - contact_ids: ["5"], + account: "6", + contacts: ["5"], probability: 20, type: "New Business", lead_source: "Web", + campaign_source: "Q1-2024-Manufacturing-Webinar", next_step: "Initial Discovery Call", description: "Full factory automation software suite." }, { _id: "105", name: "Paris Store POS System", - amount: 35000, - stage: "Qualification", + amount: 35000, + expected_revenue: 14000, + stage: "qualification", + forecast_category: "pipeline", close_date: new Date("2024-07-20"), - account_id: "7", - contact_ids: ["6"], + account: "7", + contacts: ["6"], probability: 40, type: "New Business", lead_source: "Referral", @@ -680,11 +853,13 @@ export default defineStack({ { _id: "106", name: "Tokyo E-Com Integration", - amount: 12000, - stage: "Closed Lost", + amount: 12000, + expected_revenue: 0, + stage: "closed_lost", + forecast_category: "omitted", close_date: new Date("2024-02-10"), - account_id: "5", - contact_ids: [], + account: "5", + contacts: [], probability: 0, type: "New Business", lead_source: "Web", @@ -694,14 +869,16 @@ export default defineStack({ { _id: "107", name: "SF Tower Expansion", - amount: 75000, - stage: "Closed Won", + amount: 75000, + expected_revenue: 75000, + stage: "closed_won", + forecast_category: "commit", close_date: new Date("2024-02-28"), - account_id: "2", - contact_ids: ["2"], + account: "2", + contacts: ["2"], probability: 100, type: "Upgrade", - lead_source: "Existing Business", + lead_source: "Phone", next_step: "Implement", description: "Additional storage modules." } @@ -711,55 +888,55 @@ export default defineStack({ object: 'user', mode: 'upsert', records: [ - { _id: "1", name: 'Martin CEO', email: 'martin@example.com', username: 'martin', role: 'admin', active: true }, - { _id: "2", name: 'Sarah Sales', email: 'sarah@example.com', username: 'sarah', role: 'user', active: true } + { _id: "1", name: 'Martin CEO', email: 'martin@example.com', username: 'martin', role: 'admin', title: 'Chief Executive Officer', department: 'Executive', phone: '415-555-2001', active: true }, + { _id: "2", name: 'Sarah Sales', email: 'sarah@example.com', username: 'sarah', role: 'user', title: 'Sales Manager', department: 'Sales', phone: '415-555-2002', active: true } ] }, { object: 'product', mode: 'upsert', records: [ - { _id: "p1", sku: 'HW-LAP-001', name: 'Workstation Pro Laptop', category: 'Electronics', price: 2499.99, stock: 15, image: 'https://images.unsplash.com/photo-1593642702821-c8da6771f0c6?w=600&q=80' }, - { _id: "p2", sku: 'HW-ACC-002', name: 'Wireless Ergonomic Mouse', category: 'Electronics', price: 89.99, stock: 120, image: 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=600&q=80' }, - { _id: "p3", sku: 'FUR-CHR-003', name: 'Executive Mesh Chair', category: 'Furniture', price: 549.99, stock: 8, image: 'https://images.unsplash.com/photo-1505843490538-5133c6c7d0e1?w=600&q=80' }, - { _id: "p4", sku: 'FUR-DSK-004', name: 'Adjustable Standing Desk', category: 'Furniture', price: 799.99, stock: 20, image: 'https://images.unsplash.com/photo-1595515106969-1ce29566ff1c?w=600&q=80' }, - { _id: "p5", sku: 'HW-AUD-005', name: 'Studio Noise Cancelling Headphones', category: 'Electronics', price: 349.99, stock: 45, image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600&q=80' }, - { _id: "p6", sku: 'HW-MON-006', name: '4K UltraWide Monitor', category: 'Electronics', price: 899.99, stock: 30, image: 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=600&q=80' }, - { _id: "p7", sku: 'SVC-CNS-007', name: 'Implementation Service (Hourly)', category: 'Services', price: 250.00, stock: 1000, image: 'https://images.unsplash.com/photo-1521791136064-7986c2920216?w=600&q=80' }, - { _id: "p8", sku: 'SVC-SUP-008', name: 'Premium Support (Annual)', category: 'Services', price: 5000.00, stock: 1000, image: 'https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d?w=600&q=80' } + { _id: "p1", sku: 'HW-LAP-001', name: 'Workstation Pro Laptop', category: 'electronics', price: 2499.99, stock: 15, is_active: true, manufacturer: 'TechPro', weight: 2.1, tags: ['best_seller'], image: 'https://images.unsplash.com/photo-1593642702821-c8da6771f0c6?w=600&q=80' }, + { _id: "p2", sku: 'HW-ACC-002', name: 'Wireless Ergonomic Mouse', category: 'electronics', price: 89.99, stock: 120, is_active: true, manufacturer: 'ErgoTech', weight: 0.12, tags: ['new'], image: 'https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?w=600&q=80' }, + { _id: "p3", sku: 'FUR-CHR-003', name: 'Executive Mesh Chair', category: 'furniture', price: 549.99, stock: 8, is_active: true, manufacturer: 'SitWell', weight: 15.5, image: 'https://images.unsplash.com/photo-1505843490538-5133c6c7d0e1?w=600&q=80' }, + { _id: "p4", sku: 'FUR-DSK-004', name: 'Adjustable Standing Desk', category: 'furniture', price: 799.99, stock: 20, is_active: true, manufacturer: 'StandUp Co', weight: 32.0, tags: ['best_seller'], image: 'https://images.unsplash.com/photo-1595515106969-1ce29566ff1c?w=600&q=80' }, + { _id: "p5", sku: 'HW-AUD-005', name: 'Studio Noise Cancelling Headphones', category: 'electronics', price: 349.99, stock: 45, is_active: true, manufacturer: 'SoundPro', weight: 0.35, tags: ['new', 'best_seller'], image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600&q=80' }, + { _id: "p6", sku: 'HW-MON-006', name: '4K UltraWide Monitor', category: 'electronics', price: 899.99, stock: 30, is_active: true, manufacturer: 'ViewMax', weight: 8.5, image: 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=600&q=80' }, + { _id: "p7", sku: 'SVC-CNS-007', name: 'Implementation Service (Hourly)', category: 'services', price: 250.00, stock: 1000, is_active: true, image: 'https://images.unsplash.com/photo-1521791136064-7986c2920216?w=600&q=80' }, + { _id: "p8", sku: 'SVC-SUP-008', name: 'Premium Support (Annual)', category: 'services', price: 5000.00, stock: 1000, is_active: true, tags: ['on_sale'], image: 'https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d?w=600&q=80' } ] }, { object: 'order', mode: 'upsert', records: [ - { _id: "o1", name: 'ORD-2024-001', customer: "2", order_date: new Date('2024-01-15'), amount: 15459.99, status: 'Draft' }, - { _id: "o2", name: 'ORD-2024-002', customer: "3", order_date: new Date('2024-01-18'), amount: 289.50, status: 'Pending' } + { _id: "o1", name: 'ORD-2024-001', customer: "2", account: "2", order_date: new Date('2024-01-15'), amount: 15459.99, status: 'paid', payment_method: 'Wire Transfer', shipping_address: '415 Mission St, San Francisco, CA 94105', tracking_number: '1Z999AA10123456784' }, + { _id: "o2", name: 'ORD-2024-002', customer: "3", account: "3", order_date: new Date('2024-01-18'), amount: 289.50, status: 'pending', payment_method: 'Credit Card', discount: 5 } ] }, { object: 'project_task', mode: 'upsert', records: [ - { _id: "t1", name: "Requirements Gathering", start_date: new Date("2024-02-01"), end_date: new Date("2024-02-14"), progress: 100, status: 'Completed', color: '#10b981', priority: 'High', manager: "1" }, - { _id: "t2", name: "Architecture Design", start_date: new Date("2024-02-15"), end_date: new Date("2024-03-01"), progress: 100, status: 'Completed', color: '#3b82f6', priority: 'High', dependencies: 't1', manager: "1" }, - { _id: "t3", name: "Frontend Development", start_date: new Date("2024-03-02"), end_date: new Date("2024-04-15"), progress: 75, status: 'In Progress', color: '#8b5cf6', priority: 'High', dependencies: 't2', manager: "2" }, - { _id: "t4", name: "Backend API Integration", start_date: new Date("2024-03-02"), end_date: new Date("2024-04-10"), progress: 80, status: 'In Progress', color: '#6366f1', priority: 'High', dependencies: 't2', manager: "2" }, - { _id: "t5", name: "QA & Testing", start_date: new Date("2024-04-16"), end_date: new Date("2024-05-01"), progress: 0, status: 'Planned', color: '#f59e0b', priority: 'Medium', dependencies: 't3,t4', manager: "1" }, - { _id: "t6", name: "UAT", start_date: new Date("2024-05-02"), end_date: new Date("2024-05-15"), progress: 0, status: 'Planned', color: '#f43f5e', priority: 'Medium', dependencies: 't5', manager: "1" }, - { _id: "t7", name: "Go Live & Launch", start_date: new Date("2024-05-20"), end_date: new Date("2024-05-20"), progress: 0, status: 'Planned', color: '#ef4444', priority: 'Critical', dependencies: 't6', manager: "1" } + { _id: "t1", name: "Requirements Gathering", start_date: new Date("2024-02-01"), end_date: new Date("2024-02-14"), progress: 100, estimated_hours: 40, actual_hours: 38, status: 'completed', color: '#10b981', priority: 'high', manager: "1", assignee: "2" }, + { _id: "t2", name: "Architecture Design", start_date: new Date("2024-02-15"), end_date: new Date("2024-03-01"), progress: 100, estimated_hours: 60, actual_hours: 55, status: 'completed', color: '#3b82f6', priority: 'high', dependencies: 't1', manager: "1", assignee: "1" }, + { _id: "t3", name: "Frontend Development", start_date: new Date("2024-03-02"), end_date: new Date("2024-04-15"), progress: 75, estimated_hours: 120, actual_hours: 90, status: 'in_progress', color: '#8b5cf6', priority: 'high', dependencies: 't2', manager: "2", assignee: "2" }, + { _id: "t4", name: "Backend API Integration", start_date: new Date("2024-03-02"), end_date: new Date("2024-04-10"), progress: 80, estimated_hours: 100, actual_hours: 80, status: 'in_progress', color: '#6366f1', priority: 'high', dependencies: 't2', manager: "2", assignee: "1" }, + { _id: "t5", name: "QA & Testing", start_date: new Date("2024-04-16"), end_date: new Date("2024-05-01"), progress: 0, estimated_hours: 50, status: 'planned', color: '#f59e0b', priority: 'medium', dependencies: 't3,t4', manager: "1" }, + { _id: "t6", name: "UAT", start_date: new Date("2024-05-02"), end_date: new Date("2024-05-15"), progress: 0, estimated_hours: 30, status: 'planned', color: '#f43f5e', priority: 'medium', dependencies: 't5', manager: "1" }, + { _id: "t7", name: "Go Live & Launch", start_date: new Date("2024-05-20"), end_date: new Date("2024-05-20"), progress: 0, estimated_hours: 8, status: 'planned', color: '#ef4444', priority: 'critical', dependencies: 't6', manager: "1" } ] }, { object: 'event', mode: 'upsert', records: [ - { _id: "e1", subject: "Weekly Standup", start: new Date("2024-02-05T09:00:00"), end: new Date("2024-02-05T10:00:00"), location: "Conference Room A", type: "Meeting", description: "Team synchronization regarding Project Alpha" }, - { _id: "e2", subject: "Client Call - TechCorp", start: new Date("2024-02-06T14:00:00"), end: new Date("2024-02-06T15:00:00"), location: "Zoom", type: "Call", description: "Reviewing Q1 Goals and Roadblocks" }, - { _id: "e3", subject: "Project Review", start: new Date("2024-02-08T10:00:00"), end: new Date("2024-02-08T11:30:00"), location: "Board Room", type: "Meeting", description: "Milestone review with stakeholders" }, - { _id: "e4", subject: "Lunch with Partners", start: new Date("2024-02-09T12:00:00"), end: new Date("2024-02-09T13:30:00"), location: "Downtown Cafe", type: "Other", description: "Networking event" }, - { _id: "e5", subject: "Product Demo - Berlin Auto", start: new Date("2024-03-10T11:00:00"), end: new Date("2024-03-10T12:30:00"), location: "Online", type: "Meeting", description: "Showcasing the new automation suite capabilities" }, - { _id: "e6", subject: "Internal Training", start: new Date("2024-03-15T09:00:00"), end: new Date("2024-03-15T16:00:00"), location: "Training Center", type: "Other", description: "Security compliance training for all staff" } + { _id: "e1", subject: "Weekly Standup", start: new Date("2024-02-05T09:00:00"), end: new Date("2024-02-05T10:00:00"), location: "Conference Room A", type: "meeting", status: "completed", organizer: "1", reminder: "min_15", description: "Team synchronization regarding Project Alpha" }, + { _id: "e2", subject: "Client Call - TechCorp", start: new Date("2024-02-06T14:00:00"), end: new Date("2024-02-06T15:00:00"), location: "Zoom", type: "call", status: "completed", organizer: "2", reminder: "min_5", description: "Reviewing Q1 Goals and Roadblocks" }, + { _id: "e3", subject: "Project Review", start: new Date("2024-02-08T10:00:00"), end: new Date("2024-02-08T11:30:00"), location: "Board Room", type: "meeting", status: "completed", organizer: "1", reminder: "min_30", description: "Milestone review with stakeholders" }, + { _id: "e4", subject: "Lunch with Partners", start: new Date("2024-02-09T12:00:00"), end: new Date("2024-02-09T13:30:00"), location: "Downtown Cafe", type: "other", status: "completed", organizer: "2", description: "Networking event" }, + { _id: "e5", subject: "Product Demo - Berlin Auto", start: new Date("2024-03-10T11:00:00"), end: new Date("2024-03-10T12:30:00"), location: "Online", type: "meeting", status: "scheduled", organizer: "1", reminder: "hour_1", is_private: false, description: "Showcasing the new automation suite capabilities" }, + { _id: "e6", subject: "Internal Training", start: new Date("2024-03-15T09:00:00"), end: new Date("2024-03-15T16:00:00"), location: "Training Center", type: "other", status: "scheduled", organizer: "1", is_all_day: true, reminder: "day_1", description: "Security compliance training for all staff" } ] } ] diff --git a/examples/crm/src/objects/account.object.ts b/examples/crm/src/objects/account.object.ts index ab1bccf9f..e42bf3cc8 100644 --- a/examples/crm/src/objects/account.object.ts +++ b/examples/crm/src/objects/account.object.ts @@ -4,19 +4,39 @@ export const AccountObject = ObjectSchema.create({ name: 'account', label: 'Account', icon: 'building-2', + description: 'Company and organization records for customer relationship management', fields: { name: Field.text({ label: 'Account Name', required: true, searchable: true }), - industry: Field.select(['Technology', 'Finance', 'Healthcare', 'Retail', 'Manufacturing'], { label: 'Industry' }), - rating: Field.select(['Hot', 'Warm', 'Cold'], { label: 'Rating' }), - type: Field.select(['Customer', 'Partner', 'Reseller', 'Vendor'], { label: 'Type' }), + industry: Field.select(['Technology', 'Finance', 'Healthcare', 'Retail', 'Manufacturing', 'Services'], { label: 'Industry' }), + rating: Field.select([ + { value: 'hot', label: 'Hot', color: 'red' }, + { value: 'warm', label: 'Warm', color: 'yellow' }, + { value: 'cold', label: 'Cold', color: 'blue' }, + ], { label: 'Rating' }), + type: Field.select(['Customer', 'Partner', 'Reseller', 'Vendor'], { label: 'Type', defaultValue: 'Customer' }), annual_revenue: Field.currency({ label: 'Annual Revenue' }), website: Field.url({ label: 'Website' }), - phone: Field.text({ label: 'Phone' }), + phone: Field.phone({ label: 'Phone' }), employees: Field.number({ label: 'Employees' }), billing_address: Field.textarea({ label: 'Billing Address' }), + shipping_address: Field.textarea({ label: 'Shipping Address' }), + description: Field.richtext({ label: 'Description' }), + tags: Field.select({ + options: [ + { label: 'Enterprise', value: 'enterprise', color: 'purple' }, + { label: 'SMB', value: 'smb', color: 'blue' }, + { label: 'Startup', value: 'startup', color: 'green' }, + { label: 'Government', value: 'government', color: 'gray' }, + { label: 'Strategic', value: 'strategic', color: 'red' }, + ], + multiple: true, + label: 'Tags', + }), + linkedin_url: Field.url({ label: 'LinkedIn' }), + founded_date: Field.date({ label: 'Founded Date' }), latitude: Field.number({ label: 'Latitude', scale: 6 }), longitude: Field.number({ label: 'Longitude', scale: 6 }), owner: Field.lookup('user', { label: 'Owner' }), - created_at: Field.datetime({ label: 'Created Date' }) + created_at: Field.datetime({ label: 'Created Date', readonly: true }) } }); diff --git a/examples/crm/src/objects/contact.object.ts b/examples/crm/src/objects/contact.object.ts index 49ff78cd7..a5427891c 100644 --- a/examples/crm/src/objects/contact.object.ts +++ b/examples/crm/src/objects/contact.object.ts @@ -4,21 +4,34 @@ export const ContactObject = ObjectSchema.create({ name: 'contact', label: 'Contact', icon: 'user', + description: 'Individual people associated with accounts and opportunities', fields: { name: Field.text({ label: 'Name', required: true, searchable: true }), - company: Field.text({ label: 'Company' }), - email: Field.email({ label: 'Email', searchable: true }), - phone: Field.text({ label: 'Phone' }), - title: Field.text({ label: 'Title' }), + avatar: Field.avatar({ label: 'Avatar' }), + company: Field.text({ label: 'Company', searchable: true }), + email: Field.email({ label: 'Email', searchable: true, unique: true }), + phone: Field.phone({ label: 'Phone' }), + title: Field.text({ label: 'Job Title' }), department: Field.text({ label: 'Department' }), account: Field.lookup('account', { label: 'Account' }), - status: Field.select(['Active', 'Lead', 'Customer'], { label: 'Status' }), - priority: Field.select(['High', 'Medium', 'Low'], { label: 'Priority', defaultValue: 'Medium' }), + status: Field.select([ + { value: 'active', label: 'Active', color: 'green' }, + { value: 'lead', label: 'Lead', color: 'blue' }, + { value: 'customer', label: 'Customer', color: 'purple' }, + ], { label: 'Status' }), + priority: Field.select([ + { value: 'high', label: 'High', color: 'red' }, + { value: 'medium', label: 'Medium', color: 'yellow' }, + { value: 'low', label: 'Low', color: 'green' }, + ], { label: 'Priority', defaultValue: 'medium' }), + lead_source: Field.select(['Web', 'Phone', 'Partner', 'Referral', 'Trade Show', 'Other'], { label: 'Lead Source' }), + linkedin: Field.url({ label: 'LinkedIn' }), birthdate: Field.date({ label: 'Birthdate' }), address: Field.textarea({ label: 'Address' }), latitude: Field.number({ label: 'Latitude', scale: 6 }), longitude: Field.number({ label: 'Longitude', scale: 6 }), + do_not_call: Field.boolean({ label: 'Do Not Call', defaultValue: false }), is_active: Field.boolean({ label: 'Active', defaultValue: true }), - notes: Field.textarea({ label: 'Notes' }) + notes: Field.richtext({ label: 'Notes' }) } }); diff --git a/examples/crm/src/objects/event.object.ts b/examples/crm/src/objects/event.object.ts index bccb00ff4..2a3670349 100644 --- a/examples/crm/src/objects/event.object.ts +++ b/examples/crm/src/objects/event.object.ts @@ -4,13 +4,35 @@ export const EventObject = ObjectSchema.create({ name: 'event', label: 'Event', icon: 'calendar', + description: 'Meetings, calls, and calendar events with participant tracking', fields: { - subject: Field.text({ label: 'Subject', required: true }), + subject: Field.text({ label: 'Subject', required: true, searchable: true }), start: Field.datetime({ label: 'Start', required: true }), end: Field.datetime({ label: 'End', required: true }), + is_all_day: Field.boolean({ label: 'All Day Event', defaultValue: false }), location: Field.text({ label: 'Location' }), - description: Field.textarea({ label: 'Description' }), + description: Field.richtext({ label: 'Description' }), participants: Field.lookup('contact', { label: 'Participants', multiple: true }), - type: Field.select(['Meeting', 'Call', 'Email', 'Other'], { label: 'Type' }) + organizer: Field.lookup('user', { label: 'Organizer' }), + type: Field.select([ + { value: 'meeting', label: 'Meeting', color: 'blue' }, + { value: 'call', label: 'Call', color: 'green' }, + { value: 'email', label: 'Email', color: 'purple' }, + { value: 'other', label: 'Other', color: 'gray' }, + ], { label: 'Type' }), + status: Field.select([ + { value: 'scheduled', label: 'Scheduled', color: 'blue' }, + { value: 'completed', label: 'Completed', color: 'green' }, + { value: 'cancelled', label: 'Cancelled', color: 'red' }, + ], { label: 'Status', defaultValue: 'scheduled' }), + is_private: Field.boolean({ label: 'Private', defaultValue: false }), + reminder: Field.select([ + { value: 'none', label: 'None' }, + { value: 'min_5', label: '5 minutes' }, + { value: 'min_15', label: '15 minutes' }, + { value: 'min_30', label: '30 minutes' }, + { value: 'hour_1', label: '1 hour' }, + { value: 'day_1', label: '1 day' }, + ], { label: 'Reminder', defaultValue: 'min_15' }) } }); diff --git a/examples/crm/src/objects/opportunity.object.ts b/examples/crm/src/objects/opportunity.object.ts index e818bb415..a52428b15 100644 --- a/examples/crm/src/objects/opportunity.object.ts +++ b/examples/crm/src/objects/opportunity.object.ts @@ -4,9 +4,11 @@ export const OpportunityObject = ObjectSchema.create({ name: 'opportunity', label: 'Opportunity', icon: 'trending-up', + description: 'Sales deals and revenue opportunities tracked through the pipeline', fields: { name: Field.text({ label: 'Opportunity Name', required: true, searchable: true }), amount: Field.currency({ label: 'Amount' }), + expected_revenue: Field.currency({ label: 'Expected Revenue', readonly: true }), stage: Field.select([ { value: "prospecting", label: "Prospecting", color: "purple" }, { value: "qualification", label: "Qualification", color: "indigo" }, @@ -15,13 +17,20 @@ export const OpportunityObject = ObjectSchema.create({ { value: "closed_won", label: "Closed Won", color: "green" }, { value: "closed_lost", label: "Closed Lost", color: "red" }, ], { label: 'Stage' }), - close_date: Field.date({ label: 'Close Date' }), + forecast_category: Field.select([ + { value: 'pipeline', label: 'Pipeline', color: 'blue' }, + { value: 'best_case', label: 'Best Case', color: 'green' }, + { value: 'commit', label: 'Commit', color: 'purple' }, + { value: 'omitted', label: 'Omitted', color: 'gray' }, + ], { label: 'Forecast Category' }), + close_date: Field.date({ label: 'Close Date', required: true }), account: Field.lookup('account', { label: 'Account' }), contacts: Field.lookup('contact', { label: 'Contacts', multiple: true }), probability: Field.percent({ label: 'Probability' }), type: Field.select(['New Business', 'Existing Business', 'Upgrade', 'Renewal'], { label: 'Type' }), - lead_source: Field.select(['Web', 'Phone', 'Partner', 'Referral', 'Other'], { label: 'Lead Source' }), + lead_source: Field.select(['Web', 'Phone', 'Partner', 'Referral', 'Trade Show', 'Other'], { label: 'Lead Source' }), + campaign_source: Field.text({ label: 'Campaign Source' }), next_step: Field.text({ label: 'Next Step' }), - description: Field.textarea({ label: 'Description' }) + description: Field.richtext({ label: 'Description' }) } }); diff --git a/examples/crm/src/objects/order.object.ts b/examples/crm/src/objects/order.object.ts index a3df29751..9a92a7f00 100644 --- a/examples/crm/src/objects/order.object.ts +++ b/examples/crm/src/objects/order.object.ts @@ -4,11 +4,25 @@ export const OrderObject = ObjectSchema.create({ name: 'order', label: 'Order', icon: 'shopping-cart', + description: 'Customer orders with line items, shipping, and payment tracking', fields: { - name: Field.text({ label: 'Order Number', required: true, searchable: true }), + name: Field.text({ label: 'Order Number', required: true, searchable: true, unique: true }), customer: Field.lookup('contact', { label: 'Customer', required: true }), - amount: Field.currency({ label: 'Total Amount' }), - status: Field.select(['Draft', 'Pending', 'Paid', 'Shipped', 'Delivered', 'Cancelled'], { label: 'Status', defaultValue: 'Draft' }), - order_date: Field.date({ label: 'Order Date', defaultValue: 'now' }) + account: Field.lookup('account', { label: 'Account' }), + amount: Field.currency({ label: 'Total Amount', scale: 2 }), + discount: Field.percent({ label: 'Discount', scale: 1 }), + status: Field.select([ + { value: 'draft', label: 'Draft', color: 'gray' }, + { value: 'pending', label: 'Pending', color: 'yellow' }, + { value: 'paid', label: 'Paid', color: 'green' }, + { value: 'shipped', label: 'Shipped', color: 'blue' }, + { value: 'delivered', label: 'Delivered', color: 'purple' }, + { value: 'cancelled', label: 'Cancelled', color: 'red' }, + ], { label: 'Status', defaultValue: 'draft' }), + payment_method: Field.select(['Credit Card', 'Wire Transfer', 'PayPal', 'Invoice', 'Check'], { label: 'Payment Method' }), + order_date: Field.date({ label: 'Order Date', defaultValue: 'now' }), + shipping_address: Field.textarea({ label: 'Shipping Address' }), + tracking_number: Field.text({ label: 'Tracking Number' }), + notes: Field.richtext({ label: 'Notes' }) } }); diff --git a/examples/crm/src/objects/product.object.ts b/examples/crm/src/objects/product.object.ts index 06aa8aa59..c1f417cac 100644 --- a/examples/crm/src/objects/product.object.ts +++ b/examples/crm/src/objects/product.object.ts @@ -4,13 +4,32 @@ export const ProductObject = ObjectSchema.create({ name: 'product', label: 'Product', icon: 'package', + description: 'Product catalog with pricing, inventory, and categorization', fields: { name: Field.text({ label: 'Product Name', required: true, searchable: true }), - sku: Field.text({ label: 'SKU', required: true, searchable: true }), - category: Field.select(['Electronics', 'Furniture', 'Clothing', 'Services'], { label: 'Category' }), - price: Field.currency({ label: 'Price' }), + sku: Field.text({ label: 'SKU', required: true, searchable: true, unique: true }), + category: Field.select([ + { value: 'electronics', label: 'Electronics', color: 'blue' }, + { value: 'furniture', label: 'Furniture', color: 'yellow' }, + { value: 'clothing', label: 'Clothing', color: 'pink' }, + { value: 'services', label: 'Services', color: 'green' }, + ], { label: 'Category' }), + price: Field.currency({ label: 'Price', scale: 2 }), stock: Field.number({ label: 'Stock' }), - description: Field.textarea({ label: 'Description' }), + weight: Field.number({ label: 'Weight (kg)', scale: 2 }), + manufacturer: Field.text({ label: 'Manufacturer', searchable: true }), + is_active: Field.boolean({ label: 'Active', defaultValue: true }), + tags: Field.select({ + options: [ + { label: 'New Arrival', value: 'new', color: 'green' }, + { label: 'Best Seller', value: 'best_seller', color: 'purple' }, + { label: 'On Sale', value: 'on_sale', color: 'red' }, + { label: 'Clearance', value: 'clearance', color: 'yellow' }, + ], + multiple: true, + label: 'Tags', + }), + description: Field.richtext({ label: 'Description' }), image: Field.url({ label: 'Image URL' }) } }); diff --git a/examples/crm/src/objects/project.object.ts b/examples/crm/src/objects/project.object.ts index a4d518767..df9fea3fd 100644 --- a/examples/crm/src/objects/project.object.ts +++ b/examples/crm/src/objects/project.object.ts @@ -4,16 +4,30 @@ export const ProjectObject = ObjectSchema.create({ name: 'project_task', label: 'Project Task', icon: 'kanban-square', + description: 'Project tasks with scheduling, dependencies, and progress tracking', fields: { name: Field.text({ label: 'Task Name', required: true, searchable: true }), start_date: Field.date({ label: 'Start Date', required: true }), end_date: Field.date({ label: 'End Date', required: true }), progress: Field.percent({ label: 'Progress' }), - status: Field.select(['Planned', 'In Progress', 'Completed', 'On Hold'], { label: 'Status' }), - priority: Field.select(['High', 'Medium', 'Low'], { label: 'Priority' }), + estimated_hours: Field.number({ label: 'Estimated Hours', scale: 1 }), + actual_hours: Field.number({ label: 'Actual Hours', scale: 1 }), + status: Field.select([ + { value: 'planned', label: 'Planned', color: 'gray' }, + { value: 'in_progress', label: 'In Progress', color: 'blue' }, + { value: 'completed', label: 'Completed', color: 'green' }, + { value: 'on_hold', label: 'On Hold', color: 'yellow' }, + ], { label: 'Status' }), + priority: Field.select([ + { value: 'critical', label: 'Critical', color: 'red' }, + { value: 'high', label: 'High', color: 'orange' }, + { value: 'medium', label: 'Medium', color: 'yellow' }, + { value: 'low', label: 'Low', color: 'green' }, + ], { label: 'Priority' }), manager: Field.lookup('user', { label: 'Manager' }), - description: Field.textarea({ label: 'Description' }), - color: Field.text({ label: 'Color' }), // For Gantt bar color - dependencies: Field.text({ label: 'Dependencies' }), // Comma separated IDs + assignee: Field.lookup('user', { label: 'Assignee' }), + description: Field.richtext({ label: 'Description' }), + color: Field.color({ label: 'Color' }), + dependencies: Field.text({ label: 'Dependencies' }), } }); diff --git a/examples/crm/src/objects/user.object.ts b/examples/crm/src/objects/user.object.ts index 56e605761..15093ca2a 100644 --- a/examples/crm/src/objects/user.object.ts +++ b/examples/crm/src/objects/user.object.ts @@ -4,12 +4,21 @@ export const UserObject = ObjectSchema.create({ name: 'user', label: 'User', icon: 'user-check', + description: 'System users with roles and profile information', fields: { name: Field.text({ label: 'Full Name', required: true, searchable: true }), - email: Field.email({ label: 'Email', required: true, searchable: true }), - username: Field.text({ label: 'Username', required: true }), - role: Field.select(['admin', 'user', 'guest'], { label: 'Role' }), - avatar: Field.url({ label: 'Avatar URL' }), + email: Field.email({ label: 'Email', required: true, searchable: true, unique: true }), + username: Field.text({ label: 'Username', required: true, unique: true }), + role: Field.select([ + { value: 'admin', label: 'Admin', color: 'red' }, + { value: 'user', label: 'User', color: 'blue' }, + { value: 'guest', label: 'Guest', color: 'gray' }, + ], { label: 'Role', defaultValue: 'user' }), + title: Field.text({ label: 'Job Title' }), + department: Field.text({ label: 'Department' }), + phone: Field.phone({ label: 'Phone' }), + avatar: Field.avatar({ label: 'Avatar' }), + bio: Field.textarea({ label: 'Bio' }), active: Field.boolean({ label: 'Active', defaultValue: true }) } }); diff --git a/packages/plugin-charts/src/AdvancedChartImpl.tsx b/packages/plugin-charts/src/AdvancedChartImpl.tsx index db4ab64c4..44ea2a15e 100644 --- a/packages/plugin-charts/src/AdvancedChartImpl.tsx +++ b/packages/plugin-charts/src/AdvancedChartImpl.tsx @@ -106,6 +106,7 @@ export default function AdvancedChartImpl({ donut: PieChart, radar: RadarChart, scatter: ScatterChart, + combo: BarChart, }[chartType] || BarChart; console.log('📈 Rendering Chart:', { chartType, dataLength: data.length, config, series, xAxisKey }); diff --git a/packages/plugin-dashboard/src/DashboardRenderer.tsx b/packages/plugin-dashboard/src/DashboardRenderer.tsx index 2b8aa1928..507c0ceae 100644 --- a/packages/plugin-dashboard/src/DashboardRenderer.tsx +++ b/packages/plugin-dashboard/src/DashboardRenderer.tsx @@ -176,7 +176,7 @@ export const DashboardRenderer = forwardRef 0 && (
- {schema.header.actions.map((action, i) => ( + {schema.header.actions.map((action: { label: string; actionUrl?: string; actionType?: string; icon?: string }, i: number) => ( @@ -212,8 +212,8 @@ export const DashboardRenderer = forwardRef w.type === 'metric') || []; - const otherWidgets = schema.widgets?.filter(w => w.type !== 'metric') || []; + const metricWidgets = schema.widgets?.filter((w: DashboardWidgetSchema) => w.type === 'metric') || []; + const otherWidgets = schema.widgets?.filter((w: DashboardWidgetSchema) => w.type !== 'metric') || []; return (
diff --git a/packages/plugin-form/src/autoLayout.ts b/packages/plugin-form/src/autoLayout.ts index d221d1682..7f8693b8e 100644 --- a/packages/plugin-form/src/autoLayout.ts +++ b/packages/plugin-form/src/autoLayout.ts @@ -81,7 +81,7 @@ export function applyAutoColSpan(fields: FormField[], columns: number): FormFiel if (field.colSpan !== undefined) return field; // Wide field types should span full row - if (isWideFieldType(field.type)) { + if (field.type && isWideFieldType(field.type)) { return { ...field, colSpan: columns }; } diff --git a/packages/plugin-grid/src/ImportWizard.tsx b/packages/plugin-grid/src/ImportWizard.tsx index 518b9e985..48dc7d47c 100644 --- a/packages/plugin-grid/src/ImportWizard.tsx +++ b/packages/plugin-grid/src/ImportWizard.tsx @@ -216,7 +216,7 @@ const StepPreview: React.FC<{ })), [mapping, headers, fields]); const previewRows = rows.slice(0, PREVIEW_ROW_COUNT); - const rowValidations = useMemo(() => previewRows.map((row, rIdx) => { + const rowValidations = useMemo(() => previewRows.map((row, _rIdx) => { const errs: Record = {}; for (const col of mappedCols) { const raw = row[col.csvIdx] ?? ''; diff --git a/packages/plugin-kanban/src/KanbanImpl.tsx b/packages/plugin-kanban/src/KanbanImpl.tsx index d916fda78..5f1a82190 100644 --- a/packages/plugin-kanban/src/KanbanImpl.tsx +++ b/packages/plugin-kanban/src/KanbanImpl.tsx @@ -320,7 +320,7 @@ export default function KanbanBoard({ columns, onCardMove, onCardClick, classNam return } -function KanbanBoardInner({ columns, onCardMove, onCardClick, className, dnd, quickAdd, onQuickAdd, coverImageField, conditionalFormatting, swimlaneField }: KanbanBoardProps & { dnd: ReturnType | null }) { +function KanbanBoardInner({ columns, onCardMove, onCardClick, className, dnd, quickAdd, onQuickAdd, coverImageField: _coverImageField, conditionalFormatting, swimlaneField }: KanbanBoardProps & { dnd: ReturnType | null }) { const [activeCard, setActiveCard] = React.useState(null) // Persist collapsed swimlane state per swimlaneField diff --git a/packages/plugin-map/src/ObjectMap.tsx b/packages/plugin-map/src/ObjectMap.tsx index 3bd85ad13..af0d38e46 100644 --- a/packages/plugin-map/src/ObjectMap.tsx +++ b/packages/plugin-map/src/ObjectMap.tsx @@ -25,7 +25,7 @@ import type { ObjectGridSchema, DataSource, ViewData } from '@object-ui/types'; import { useNavigationOverlay } from '@object-ui/react'; import { NavigationOverlay } from '@object-ui/components'; import { z } from 'zod'; -import Map, { NavigationControl, Marker, Popup } from 'react-map-gl/maplibre'; +import MapGL, { NavigationControl, Marker, Popup } from 'react-map-gl/maplibre'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; @@ -538,7 +538,7 @@ export const ObjectMap: React.FC = ({
)}
- = ({
)} - +
{navigation.isOverlay && ( diff --git a/packages/plugin-view/src/ViewTabBar.tsx b/packages/plugin-view/src/ViewTabBar.tsx index 3eb35a309..c4444ac7b 100644 --- a/packages/plugin-view/src/ViewTabBar.tsx +++ b/packages/plugin-view/src/ViewTabBar.tsx @@ -185,7 +185,7 @@ const SortableTab: React.FC<{ opacity: isDragging ? 0.5 : undefined, }; - return <>{children({ setNodeRef, style, listeners, attributes, isDragging })}; + return <>{children({ setNodeRef, style, listeners, attributes: attributes as Record, isDragging })}; }; /**