diff --git a/docs/ai/cli-usage.md b/docs/ai/cli-usage.md index 74b84a9d..6a7eb08c 100644 --- a/docs/ai/cli-usage.md +++ b/docs/ai/cli-usage.md @@ -110,11 +110,9 @@ For a `complete` application, you get: 1. **Metadata Files (YAML)** - `*.object.yml` - Data entities - `*.validation.yml` - Validation rules - - `*.form.yml` - Data entry forms - - `*.view.yml` - List views - - `*.page.yml` - UI pages - - `*.menu.yml` - Navigation - `*.permission.yml` - Access control + - `*.workflow.yml` - Automation + - `*.data.yml` - Seed data 2. **TypeScript Implementation Files** - `*.action.ts` - Custom business operations diff --git a/docs/guide/page-metadata.md b/docs/guide/page-metadata.md deleted file mode 100644 index 02fa23be..00000000 --- a/docs/guide/page-metadata.md +++ /dev/null @@ -1,671 +0,0 @@ -# Page Metadata Guide - -Page metadata in ObjectQL allows you to define custom UI pages declaratively using YAML files. This approach is inspired by low-code platforms like Airtable, Retool, and Appsmith, making it easy to create rich, data-driven interfaces without writing custom frontend code. - -## Overview - -Pages are the visual interface layer in ObjectQL applications. They define how data is displayed, how users interact with it, and how components are arranged on the screen. - -### Key Features - -- **Declarative Configuration**: Define pages using simple YAML files -- **Multiple Layout Types**: Support for various layouts (dashboard, forms, wizards, etc.) -- **Component-Based**: Compose pages from reusable components -- **Data Binding**: Connect components to ObjectQL data sources -- **Responsive Design**: Built-in responsive configuration -- **Access Control**: Fine-grained permission management -- **AI-Ready**: AI context for intelligent page generation - -## Quick Start - -Create a new page by adding a `*.page.yml` file to your project: - -```yaml -# src/dashboard.page.yml -name: dashboard -label: Project Dashboard -description: Overview of projects and tasks -icon: dashboard -layout: dashboard - -components: - - id: total_projects - type: metric - label: Total Projects - data_source: - object: projects - query: - op: count - grid: - x: 0 - y: 0 - w: 3 - h: 2 - - - id: recent_tasks - type: data_grid - label: Recent Tasks - data_source: - object: tasks - fields: ['name', 'status', 'due_date'] - sort: [['created_at', 'desc']] - limit: 10 - grid: - x: 0 - y: 2 - w: 12 - h: 6 -``` - -## Page Configuration - -### Basic Structure - -Every page must have these core properties: - -```yaml -name: page_identifier # Unique identifier -label: Display Name # Human-readable name -layout: single_column # Layout type -``` - -### Layout Types - -ObjectQL supports multiple layout types for different use cases: - -#### 1. Single Column Layout -```yaml -layout: single_column -components: - - id: header - type: text - label: Welcome - - id: data_table - type: data_grid - data_source: - object: tasks -``` - -Best for: Simple forms, lists, detail views - -#### 2. Two Column Layout -```yaml -layout: two_column -sections: - - id: main_content - type: content - style: - width: 70% - components: - - id: edit_form - type: form - # ... - - - id: sidebar - type: sidebar - style: - width: 30% - components: - - id: stats - type: metric - # ... -``` - -Best for: Detail pages with sidebar, master-detail views - -#### 3. Dashboard Layout -```yaml -layout: dashboard -components: - - id: metric_1 - type: metric - grid: - x: 0 # Grid column (0-11) - y: 0 # Grid row - w: 3 # Width (grid units) - h: 2 # Height (grid units) - - - id: chart_1 - type: chart - grid: - x: 3 - y: 0 - w: 6 - h: 4 -``` - -Best for: Dashboards, KPI displays, analytics pages - -#### 4. Wizard Layout -```yaml -layout: wizard -components: - - id: step_1 - type: container - label: Basic Info - config: - step: 1 - components: - - id: form_1 - type: form - # ... - - - id: step_2 - type: container - label: Details - config: - step: 2 - components: - - id: form_2 - type: form - # ... -``` - -Best for: Multi-step processes, onboarding, complex forms - -#### 5. Canvas Layout -```yaml -layout: canvas -components: - - id: hero - type: container - style: - position: absolute - top: 0 - left: 0 - width: 100% - height: 400px - # ... -``` - -Best for: Landing pages, custom layouts - -#### 6. Tabs Layout -```yaml -layout: tabs -components: - - id: tab_1 - type: container - label: Overview - components: - # Tab content - - - id: tab_2 - type: container - label: Details - components: - # Tab content -``` - -Best for: Organizing related content - -## Components - -### Component Types - -ObjectQL provides a rich set of built-in component types: - -#### Data Display Components - -**Data Grid** -```yaml -- id: tasks_grid - type: data_grid - label: Tasks - data_source: - object: tasks - fields: ['name', 'status', 'priority', 'due_date'] - sort: [['created_at', 'desc']] - config: - columns: - - field: name - label: Task Name - width: 300 - - field: status - label: Status - badge: true - row_actions: - - label: Edit - action: edit_task - enable_search: true - enable_filters: true -``` - -**Detail View** -```yaml -- id: project_detail - type: detail_view - label: Project Details - data_source: - object: projects - query: - op: findOne - filter: [['_id', '=', '{{route.params.id}}']] - config: - mode: readonly - sections: - - label: Basic Info - fields: ['name', 'description', 'status'] - - label: Timeline - fields: ['start_date', 'end_date'] -``` - -**Metric/KPI** -```yaml -- id: total_count - type: metric - label: Total Projects - data_source: - object: projects - query: - op: count - config: - format: number - icon: folder - color: blue -``` - -**Chart** -```yaml -- id: status_chart - type: chart - label: Projects by Status - data_source: - object: projects - fields: ['status'] - query: - op: group_by - field: status - aggregate: count - config: - chart_type: pie - colors: ['#10b981', '#3b82f6', '#f59e0b'] -``` - -#### Data Input Components - -**Form** -```yaml -- id: edit_form - type: form - label: Edit Project - data_source: - object: projects - config: - mode: edit # create, edit, or view - layout: vertical - fields: - - name: name - label: Project Name - type: text - required: true - - name: description - label: Description - type: textarea - - name: status - label: Status - type: select - field_layout: - - row: [name] - - row: [description] - - row: [status] - actions: - on_submit: - type: run_action - object: projects - action: update - success_message: Project updated -``` - -**Button** -```yaml -- id: submit_btn - type: button - label: Submit - config: - variant: primary - icon: check - actions: - on_click: - type: submit_form - success_message: Submitted successfully -``` - -#### Layout Components - -**Container** -```yaml -- id: section_1 - type: container - label: Section Title - components: - - id: child_1 - type: text - # ... -``` - -**Tabs** -```yaml -- id: tabs_container - type: tabs - components: - - id: tab_1 - type: container - label: Tab 1 - components: [...] - - id: tab_2 - type: container - label: Tab 2 - components: [...] -``` - -#### Content Components - -**Text** -```yaml -- id: welcome_text - type: text - config: - content: | - # Welcome to ObjectQL - This is a markdown-formatted text component. - format: markdown - style: - padding: 20px -``` - -**Image** -```yaml -- id: logo - type: image - config: - src: /assets/logo.png - alt: Company Logo - style: - width: 200px -``` - -**Divider** -```yaml -- id: separator - type: divider - style: - margin: 20px 0 -``` - -## Data Sources - -Components can connect to ObjectQL data sources: - -### Basic Query -```yaml -data_source: - object: projects - fields: ['name', 'status', 'owner'] - sort: [['created_at', 'desc']] - limit: 10 -``` - -### Filtered Query -```yaml -data_source: - object: tasks - fields: ['name', 'due_date'] - filters: - - ['status', '=', 'active'] - - 'and' - - ['assigned_to', '=', '{{current_user.id}}'] -``` - -### With Relationships -```yaml -data_source: - object: tasks - fields: ['name', 'status'] - expand: - project: - fields: ['name'] - assigned_to: - fields: ['name', 'email'] -``` - -### Aggregations -```yaml -data_source: - object: projects - query: - op: count -``` - -```yaml -data_source: - object: tasks - query: - op: group_by - field: status - aggregate: count -``` - -## Actions - -Components can trigger actions in response to user interactions: - -### Navigation -```yaml -actions: - on_click: - type: navigate - path: /projects/{{id}} -``` - -### Modal -```yaml -actions: - on_click: - type: open_modal - modal: edit_project_modal -``` - -### Execute Action -```yaml -actions: - on_click: - type: run_action - object: projects - action: archive - confirm: Archive this project? - success_message: Project archived -``` - -### Form Submission -```yaml -actions: - on_submit: - type: submit_form - success_message: Form submitted - on_error: show_toast -``` - -### Refresh Data -```yaml -actions: - on_load: - type: refresh -``` - -### Custom Handler -```yaml -actions: - on_click: - type: custom - handler: myCustomHandler -``` - -## Styling - -Components support flexible styling: - -```yaml -style: - width: 100% - height: 400px - background: '#f7fafc' - padding: 20px - margin: 10px 0 - border_radius: 8px - class_name: custom-class - custom_css: - box-shadow: 0 2px 4px rgba(0,0,0,0.1) -``` - -## Responsive Design - -Configure responsive behavior for different screen sizes: - -### Page-Level -```yaml -responsive: - mobile: - columns: 1 - tablet: - columns: 2 - desktop: - columns: 3 -``` - -### Component-Level -```yaml -components: - - id: responsive_grid - type: data_grid - responsive: - mobile: - visible: true - columns: 1 - tablet: - visible: true - columns: 2 - desktop: - visible: true - columns: 3 -``` - -## Access Control - -Define permissions for pages and components: - -### Page Permissions -```yaml -permissions: - view: ['admin', 'manager', 'user'] - edit: ['admin', 'manager'] -``` - -### Component Visibility -```yaml -components: - - id: admin_only - type: button - label: Delete - permissions: ['admin'] - visible_when: - role: admin -``` - -## State Management - -Pages can maintain state: - -```yaml -state: - initial: - current_tab: 0 - selected_items: [] - filter_value: '' - persist: true - storage_key: my_page_state -``` - -## AI Context - -Provide context for AI-powered features: - -```yaml -ai_context: - intent: Manage project tasks and track progress - persona: Project managers and team members - tasks: - - View all tasks - - Create new tasks - - Update task status - - Assign tasks to team members -``` - -## Real-time Updates - -Enable real-time data updates: - -```yaml -realtime: true -refresh_interval: 30 # seconds -``` - -## SEO & Metadata - -Configure SEO metadata: - -```yaml -meta: - title: Project Dashboard - My App - description: Manage your projects and tasks - keywords: ['projects', 'tasks', 'dashboard'] -``` - -## Examples - -### Complete Dashboard Example - -See `packages/starters/basic/src/dashboard.page.yml` for a full dashboard implementation. - -### Form Page Example - -See `packages/starters/basic/src/project_detail.page.yml` for a two-column detail page with forms. - -### Wizard Example - -See `packages/starters/basic/src/create_project_wizard.page.yml` for a multi-step wizard. - -### Canvas Layout Example - -See `packages/starters/basic/src/landing.page.yml` for a custom landing page. - -## Integration with Navigation - -Pages can be referenced in application navigation: - -```yaml -# app.yml -navigation: - - type: page - name: dashboard - label: Dashboard - icon: dashboard - path: /dashboard -``` - -## Best Practices - -1. **Keep It Simple**: Start with simple layouts and add complexity as needed -2. **Reuse Components**: Use containers to create reusable component groups -3. **Data Binding**: Use `{{}}` syntax to bind dynamic values -4. **Responsive First**: Always consider mobile layouts -5. **Test Permissions**: Verify access control for sensitive pages -6. **AI Context**: Provide clear AI context for better code generation -7. **Naming Convention**: Use descriptive component IDs (e.g., `tasks_grid` not `grid1`) - -## Next Steps - -- Learn about [Application Configuration](./application.md) -- Explore [Data Modeling](./data-modeling.md) -- Read about [Actions](./logic-actions.md) -- Check [Validation Rules](./validation.md) diff --git a/docs/spec/app.md b/docs/spec/app.md index c3e5084a..66869ab0 100644 --- a/docs/spec/app.md +++ b/docs/spec/app.md @@ -38,11 +38,9 @@ is_active: true | `sort_no` | number | No | Integer for sorting applications in the launcher. | | `is_active` | boolean | No | Whether the app is visible to users. Defaults to `true`. | -## 4. Relationship with Menus +## 4. Application Usage -Menus are defined separately in `*.menu.yml` files and can be linked to an application. This separation allows for: -- Reusing menus across applications. -- Keeping the application definition lightweight. -- Frontend plugins to dynamically inject or modify menus without touching the app definition. - -See [Menu Metadata](./menu.md) for details on defining structure and items. +Application metadata serves as a container for grouping related functionality. The application definition is intentionally kept simple and lightweight to allow for: +- Organizing related objects, workflows, and actions +- Defining application-level configuration +- Frontend plugins to dynamically integrate with the application without modifying core definitions diff --git a/docs/spec/form.md b/docs/spec/form.md deleted file mode 100644 index 106d346a..00000000 --- a/docs/spec/form.md +++ /dev/null @@ -1,1046 +0,0 @@ -# Form Metadata - -Form metadata defines the structure, layout, and behavior of data entry forms. Forms are the primary interface for creating and editing records. They are designed to be both functional and AI-understandable for automated form generation. - -## 1. Overview - -Form metadata provides: - -- **Layout definition**: Multi-column layouts, sections, tabs, wizards -- **Field configuration**: Labels, help text, default values, placeholders -- **Conditional logic**: Show/hide fields based on conditions -- **Validation**: Real-time field validation with clear messages -- **Workflow integration**: Multi-step forms, wizards -- **Responsive design**: Mobile-optimized layouts -- **AI assistance**: Smart defaults, auto-complete, validation helpers - -**File Naming Convention:** `.form.yml` - -The filename (without the `.form.yml` extension) automatically becomes the form's identifier. This eliminates the need for a redundant `name` property. - -**Examples:** -- `project_form.form.yml` → Form name: `project_form` -- `quick_task.form.yml` → Form name: `quick_task` -- `customer_edit.form.yml` → Form name: `customer_edit` - -## 2. Root Structure with AI Context - -```yaml -# File: project_form.form.yml -# Form name is inferred from filename! - -label: Project Information -type: edit # create, edit, view -object: projects -description: Form for creating and editing projects - -# AI-friendly context -ai_context: - intent: "Capture essential project information for planning and tracking" - user_persona: "Project manager or team lead" - typical_completion_time: "3-5 minutes" - - # Help AI understand form flow - user_journey: | - 1. Enter basic project details (name, description) - 2. Set timeline and budget - 3. Assign team members - 4. Review and submit - - # Common issues to prevent - common_errors: - - "Forgetting to set realistic deadlines" - - "Not assigning budget upfront" - - "Missing team member assignments" - - # Quality checks - quality_guidance: | - - Name should be descriptive and unique - - Description should explain problem being solved - - Budget should align with project scope - -# Layout Configuration -layout: - columns: 2 - sections: - - name: basic_info - label: Basic Information - columns: 2 - - ai_context: - intent: "Capture core identifying information" - user_guidance: "Start with a clear, descriptive project name" - completion_order: 1 - - fields: - - name - - status - - priority - - category - - - name: details - label: Project Details - columns: 1 - - ai_context: - intent: "Document project purpose and scope" - user_guidance: "Be specific about what problems this project solves" - completion_order: 2 - - fields: - - description - - objectives - - deliverables - - - name: timeline - label: Timeline & Budget - columns: 2 - - ai_context: - intent: "Set realistic expectations for duration and cost" - validation_focus: "Ensure dates are logical and budget is within limits" - completion_order: 3 - - fields: - - start_date - - end_date - - budget - - estimated_hours - - - name: assignment - label: Team Assignment - columns: 2 - - ai_context: - intent: "Assign ownership and build project team" - user_guidance: "Owner is accountable for project success" - completion_order: 4 - - fields: - - owner - - team - - department - -# Field Overrides with AI Enhancement -field_config: - name: - placeholder: "e.g., 'Q2 Website Redesign'" - help_text: "Choose a name that clearly identifies the project" - - ai_context: - intent: "Unique project identifier for tracking and reporting" - - # Smart suggestions - suggestions_based_on: - - "Department name" - - "Current quarter" - - "Primary deliverable" - - quality_check: "Should be descriptive, not generic like 'Project 1'" - - examples: - good: ["Website Redesign 2026", "Q1 Marketing Campaign"] - bad: ["Project", "Untitled", "New Project"] - - description: - rows: 5 - placeholder: "What is this project about? What problem does it solve?" - help_text: "Provide comprehensive project overview" - - ai_context: - intent: "Clear problem statement and expected outcomes" - quality_check: "Should answer: What, Why, and Expected Outcome" - min_quality_length: 50 # Characters for meaningful description - - ai_assist: - enabled: true - feature: "auto_expand" - prompt: "Expand this brief description into a full project overview" - - status: - default_value: planning - help_text: "Current project stage" - - ai_context: - intent: "Track project through lifecycle" - default_rationale: "New projects start in planning phase" - - budget: - prefix: $ - format: currency - help_text: "Total approved project budget" - - ai_context: - intent: "Financial planning and tracking" - - # Smart defaults based on project type - smart_defaults: - small_project: 25000 - medium_project: 100000 - large_project: 500000 - - # Warning thresholds (severity: info, warning, error) - warning_thresholds: - - amount: 50000 - message: "Projects over $50K require manager approval" - severity: warning - - - amount: 200000 - message: "Projects over $200K require director approval" - severity: error - - start_date: - default: "$today" - - ai_context: - intent: "When project work begins" - smart_default_rationale: "Most projects start immediately or within days" - - end_date: - ai_context: - intent: "Target completion date" - - # AI can suggest realistic timelines - suggestions: - - value: "start_date + 30 days" - label: "1 month project" - - value: "start_date + 90 days" - label: "1 quarter project" - - value: "start_date + 180 days" - label: "6 month project" - - owner: - default: "$current_user" - - ai_context: - intent: "Person responsible for project success" - default_rationale: "Usually the person creating the project" - - # Smart suggestions - suggestions_based_on: - - "Current user's manager" - - "Department head" - - "Previous project owners in same category" - -# Conditional Display with Business Logic -conditional_logic: - - name: show_completion_fields - - ai_context: - intent: "Show completion tracking when project is done" - business_rule: "Completed projects need actual dates and costs" - - condition: - field: status - operator: "=" - value: completed - - actions: - - show_fields: [completion_date, actual_cost, completion_notes] - - mark_required: [completion_date, completion_notes] - - hide_fields: [estimated_hours] - - - name: require_approval_info - - ai_context: - intent: "High-value projects need approval tracking" - business_rule: "Company policy requires approval for projects > $50K" - - condition: - field: budget - operator: ">" - value: 50000 - - actions: - - show_fields: [approval_required, approver, approval_justification] - - mark_required: [approver, approval_justification] - - show_message: - type: info - text: "This project requires management approval due to budget amount" - - - name: suggest_risk_assessment - - ai_context: - intent: "Large or complex projects should assess risks" - business_rule: "Projects over 6 months or $200K should document risks" - - condition: - any_of: - - field: budget - operator: ">" - value: 200000 - - field: estimated_hours - operator: ">" - value: 1000 - - actions: - - show_section: risk_assessment - - show_message: - type: warning - text: "Consider documenting project risks and mitigation strategies" - -# AI-Powered Form Features -ai_features: - # Auto-complete from context - auto_complete: - enabled: true - fields: [description, objectives] - - ai_context: - intent: "Help users write better project descriptions" - feature: "Expand brief notes into full descriptions" - - # Smart validation - validation_assistant: - enabled: true - - ai_context: - intent: "Real-time suggestions for improving data quality" - checks: - - "Description is too vague" - - "Budget seems unrealistic for project scope" - - "Timeline conflicts with resource availability" - - # Similar projects - similar_projects: - enabled: true - match_on: [category, department, budget_range] - - ai_context: - intent: "Show similar past projects to help with estimation" - use: "User can copy timeline and budget estimates from successful projects" - -# Actions -actions: - primary: - - label: Save - action: save - icon: save - - label: Save & New - action: save_and_new - icon: add - - secondary: - - label: Cancel - action: cancel - icon: close - -# Validation -validation: - on_change: true - on_submit: true - show_errors_inline: true -``` - -## 3. Form Types - -| Type | Description | Use Case | -|:---|:---|:---| -| `create` | New record creation | Creating new entities | -| `edit` | Record editing | Updating existing records | -| `view` | Read-only display | Viewing record details | -| `wizard` | Multi-step form | Complex data entry workflows | -| `inline` | Inline editing | Quick edits in lists/grids | -| `quick_create` | Minimal quick form | Fast record creation | - -## 4. Layout Sections - -Organize fields into logical groups: - -```yaml -layout: - # Global layout settings - columns: 2 - spacing: medium - - sections: - # Basic Section - - name: personal_info - label: Personal Information - description: Employee basic details - columns: 2 - collapsible: true - collapsed: false - fields: - - first_name - - last_name - - email - - phone - - # Full-width Section - - name: address - label: Address - columns: 1 - fields: - - street - - city - - state - - zip_code - - # Nested Sections - - name: employment - label: Employment Details - sections: - - name: position - label: Position - fields: - - title - - department - - manager - - - name: compensation - label: Compensation - fields: - - salary - - bonus - - stock_options -``` - -## 5. Field Configuration - -Override object-level field definitions: - -```yaml -field_config: - # Text Field - email: - label: Email Address - placeholder: user@example.com - help_text: Primary contact email - required: true - autocomplete: email - validation: - format: email - - # Number Field - budget: - label: Project Budget - prefix: $ - suffix: USD - format: currency - decimal_places: 2 - min: 0 - max: 1000000 - step: 1000 - help_text: Total allocated budget - - # Select Field - status: - label: Status - help_text: Current project status - default_value: draft - style: dropdown # dropdown, radio, buttons - - # Lookup Field - owner: - label: Project Owner - help_text: Responsible person - search_fields: [name, email] - display_format: "{name} ({email})" - create_new: true # Allow creating new record - - # Date Field - start_date: - label: Start Date - help_text: Project kickoff date - min_date: today - max_date: +1 year - default_value: today - format: MM/DD/YYYY - - # Textarea - description: - label: Description - placeholder: Enter detailed description - rows: 5 - max_length: 5000 - show_character_count: true - rich_text: false - - # Checkbox - is_public: - label: Public Project - help_text: Visible to all users - default_value: false - - # File Upload - attachments: - label: Attachments - help_text: Upload relevant documents - multiple: true - accept: [.pdf, .doc, .docx, .xls, .xlsx] - max_size: 10MB - max_files: 5 -``` - -## 6. Conditional Logic - -Show/hide fields and control behavior based on conditions: - -```yaml -conditional_logic: - # Show/Hide Fields - - name: show_international_fields - condition: - field: country - operator: "!=" - value: US - actions: - - show_fields: [passport_number, visa_status] - - hide_fields: [ssn] - - # Make Fields Required - - name: require_reason_for_delay - condition: - field: status - operator: "=" - value: delayed - actions: - - mark_required: [delay_reason, estimated_recovery_date] - - # Change Field Properties - - name: readonly_after_approval - condition: - field: approval_status - operator: "=" - value: approved - actions: - - readonly_fields: [budget, scope, timeline] - - # Multiple Conditions (AND) - - name: high_value_approval - condition: - type: and - conditions: - - field: amount - operator: ">" - value: 10000 - - field: department - operator: "=" - value: finance - actions: - - show_fields: [cfo_approval] - - mark_required: [cfo_approval] - - # Multiple Conditions (OR) - - name: special_handling - condition: - type: or - conditions: - - field: priority - operator: "=" - value: urgent - - field: vip_customer - operator: "=" - value: true - actions: - - show_section: special_instructions - - set_field_value: - field: requires_review - value: true -``` - -## 7. Tabs Layout - -Organize complex forms into tabs: - -```yaml -layout: - type: tabs - tabs: - # Tab 1: Basic Info - - name: basic - label: Basic Information - icon: info - sections: - - name: details - fields: - - name - - description - - status - - # Tab 2: Advanced - - name: advanced - label: Advanced Settings - icon: settings - sections: - - name: configuration - fields: - - priority - - tags - - custom_fields - - # Tab 3: Related Records - - name: related - label: Related Items - icon: link - sections: - - name: tasks - type: related_list - object: tasks - relation: project_id - - - name: documents - type: related_list - object: documents - relation: project_id -``` - -## 8. Wizard Forms - -Multi-step forms for complex workflows: - -```yaml -name: project_wizard -type: wizard -object: projects - -steps: - # Step 1: Basic Info - - name: basic - label: Basic Information - description: Enter project fundamentals - fields: - - name - - description - - category - validation: - required: [name, category] - - # Show only if validation passes - allow_next: true - - # Step 2: Team - - name: team - label: Team Assignment - description: Assign team members - fields: - - owner - - team_members - - department - - # Skip if single-person project - skip_if: - field: project_type - operator: "=" - value: individual - - # Step 3: Timeline - - name: timeline - label: Timeline & Budget - description: Set schedule and budget - fields: - - start_date - - end_date - - budget - - milestones - - # Step 4: Review - - name: review - label: Review & Submit - description: Review all information - type: summary - - # Show summary of all fields - summary_sections: - - label: Basic Information - fields: [name, description, category] - - label: Team - fields: [owner, team_members] - - label: Timeline - fields: [start_date, end_date, budget] - -# Wizard Navigation -wizard_config: - allow_back: true - allow_skip: false - show_progress: true - save_draft: true - - buttons: - back: Previous - next: Next - finish: Create Project -``` - -## 9. Field Groups - -Group related fields visually: - -```yaml -layout: - sections: - - name: contact - label: Contact Information - - # Field Groups within Section - field_groups: - # Primary Contact - - label: Primary Contact - columns: 2 - fields: - - primary_email - - primary_phone - - # Secondary Contact - - label: Secondary Contact - columns: 2 - collapsible: true - collapsed: true - fields: - - secondary_email - - secondary_phone -``` - -## 10. Inline Editing - -Quick edit configuration: - -```yaml -name: task_inline_edit -type: inline -object: tasks - -# Fields available for inline edit -editable_fields: - - status - - priority - - assignee - - due_date - -# Quick action buttons -quick_actions: - - label: Complete - action: update - values: - status: completed - completed_date: $current_date - - - label: Reassign - action: show_field - field: assignee -``` - -## 11. Quick Create Form - -Minimal form for fast data entry: - -```yaml -name: task_quick_create -type: quick_create -object: tasks -label: Quick Add Task - -# Minimal required fields -fields: - - name - - assignee - - due_date - -# Auto-fill fields -defaults: - status: open - priority: medium - created_by: $current_user - -# After creation -after_create: - action: close # or 'stay', 'redirect' - message: Task created successfully -``` - -## 12. Dynamic Fields - -Add/remove field groups dynamically: - -```yaml -layout: - sections: - - name: contacts - label: Contact Persons - type: repeatable - - # Field group that can be repeated - field_group: - - name - - email - - phone - - role - - # Configuration - min_items: 1 - max_items: 10 - add_button_label: Add Contact - remove_button_label: Remove -``` - -## 13. Form Actions - -Define available actions on the form: - -```yaml -actions: - # Primary Actions (prominent buttons) - primary: - - label: Save - action: save - icon: save - hotkey: Ctrl+S - - - label: Save & Close - action: save_and_close - icon: save_close - - # Secondary Actions (less prominent) - secondary: - - label: Save as Draft - action: save - values: - status: draft - icon: draft - - - label: Cancel - action: cancel - icon: close - confirm: Discard changes? - - # Custom Actions - custom: - - label: Send for Approval - action: custom - handler: send_for_approval - icon: send - - # Only show when ready - visible_when: - field: status - operator: "=" - value: ready_for_approval -``` - -## 14. Form Validation - -```yaml -validation: - # When to validate - on_change: true # Validate as user types - on_blur: true # Validate when field loses focus - on_submit: true # Validate before submit - - # How to show errors - show_errors_inline: true - show_error_summary: true - error_summary_position: top - - # Validation rules - rules: - - fields: [start_date, end_date] - validator: | - function validate(values) { - return values.end_date > values.start_date; - } - message: End date must be after start date -``` - -## 15. Responsive Design - -Mobile-optimized layouts: - -```yaml -layout: - # Desktop layout - desktop: - columns: 2 - sections: [...] - - # Tablet layout - tablet: - columns: 1 - compact_sections: true - - # Mobile layout - mobile: - columns: 1 - stack_fields: true - hide_labels: false - - # Show only essential fields - visible_fields: - - name - - status - - priority - - due_date -``` - -## 16. Field Dependencies - -Load field options based on other field values: - -```yaml -field_config: - state: - type: select - label: State - - city: - type: select - label: City - depends_on: state - - # Load cities based on selected state - options_query: - object: cities - filters: - - field: state - operator: "=" - value: $state - fields: [name] - sort: - - field: name - direction: asc -``` - -## 17. Auto-save - -Automatically save form progress: - -```yaml -auto_save: - enabled: true - interval: 30000 # 30 seconds - mode: draft # Save as draft - - # Show indicator - show_indicator: true - indicator_position: top-right - - # Recovery - enable_recovery: true - recovery_prompt: You have unsaved changes. Restore? -``` - -## 18. Form Events & Hooks - -Execute logic on form events: - -```yaml -events: - # Before form loads - on_load: | - async function onLoad(context) { - // Pre-populate fields - if (context.user.department) { - context.setFieldValue('department', context.user.department); - } - } - - # When field changes - on_field_change: - budget: - handler: | - function onChange(value, context) { - // Auto-calculate tax - const tax = value * 0.1; - context.setFieldValue('estimated_tax', tax); - } - - # Before submit - on_before_submit: | - async function beforeSubmit(values, context) { - // Final validation - if (values.amount > 10000 && !values.approval) { - context.showError('High-value orders require approval'); - return false; - } - return true; - } - - # After successful submit - on_success: | - function onSuccess(result, context) { - context.showMessage('Record saved successfully'); - context.redirect(`/projects/${result.id}`); - } - - # On error - on_error: | - function onError(error, context) { - context.showError(`Failed to save: ${error.message}`); - } -``` - -## 19. Form Modes - -Different modes for different contexts: - -```yaml -modes: - # Create mode - create: - title: New Project - show_fields: [name, description, owner, start_date] - defaults: - status: draft - created_by: $current_user - - # Edit mode - edit: - title: Edit Project - show_fields: [name, description, status, owner, start_date, end_date] - readonly_fields: [created_by, created_at] - - # Clone mode - clone: - title: Clone Project - show_fields: [name, description, owner] - exclude_fields: [id, created_at, created_by] - field_overrides: - name: - default_value: "Copy of {original_name}" -``` - -## 20. Implementation Example - -```typescript -// src/forms/project.form.yml -import { FormDefinition } from '@objectql/types'; - -export const project_form: FormDefinition = { - name: 'project_form', - type: 'edit', - object: 'projects', - layout: { - columns: 2, - sections: [ - { - name: 'basic', - label: 'Basic Information', - fields: ['name', 'description', 'status'] - } - ] - }, - field_config: { - status: { - default_value: 'draft' - } - } -}; -``` - -## 21. Best Practices - -1. **User Experience**: Group related fields logically -2. **Progressive Disclosure**: Show advanced fields only when needed -3. **Clear Labels**: Use descriptive labels and help text -4. **Validation**: Validate early and provide clear error messages -5. **Performance**: Lazy-load heavy components -6. **Accessibility**: Support keyboard navigation and screen readers -7. **Mobile**: Design for mobile-first -8. **Defaults**: Set sensible default values - -## 22. Related Specifications - -- [Objects & Fields](./object.md) - Data model -- [Views](./view.md) - Display layouts -- [Validation](./validation.md) - Validation rules -- [Actions](./action.md) - Form actions diff --git a/docs/spec/index.md b/docs/spec/index.md index 950ef8b8..be898db5 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -18,14 +18,6 @@ This section contains the complete metadata specifications for the ObjectQL plat * [**Actions (RPC)**](./action.md) - Custom server-side functions and APIs ✅ * [**Workflows & Processes**](./workflow.md) - Automated business processes and approval chains ⚠️ *Spec complete, implementation pending* -### Presentation Layer -* [**Applications**](./app.md) - Application container and simple metadata ✅ -* [**Pages**](./page.md) - Composable UI pages with layouts, components, and interactions ✅ -* [**Menus & Navigation**](./menu.md) - Application structure and navigation hierarchy ✅ -* [**Views & Layouts**](./view.md) - Data presentation configurations (list, grid, kanban, calendar) ⚠️ *Spec complete, implementation pending* -* [**Forms**](./form.md) - Data entry and editing interfaces ⚠️ *Spec complete, implementation pending* -* [**Reports & Dashboards**](./report.md) - Analytics and BI ⚠️ *Spec complete, implementation pending* - ## Security & Access Control * [**Permissions**](./permission.md) - Role-based access control, field-level security, and record-level rules ✅ @@ -60,29 +52,13 @@ src/ workflows/ # Business processes *.workflow.yml # Workflow definitions - pages/ # UI pages - *.page.yml # Page definitions - - views/ # UI presentation - *.view.yml # View configurations - *.form.yml # Form layouts - - reports/ # Analytics - *.report.yml # Report definitions - *.dashboard.yml # Dashboard configurations - data/ # Initial Seeding *.data.yml # Seed data - - navigation/ # App structure - *.menu.yml # Menu definitions ``` ## Getting Started 1. **Start with Objects**: Define your data model using [Objects & Fields](./object.md) 2. **Add Business Logic**: Implement validation, hooks, and actions -3. **Design UI**: Create views, forms, and navigation -4. **Secure Your App**: Configure permissions and access rules -5. **Build Workflows**: Automate processes and approvals -6. **Create Reports**: Add analytics and dashboards +3. **Secure Your App**: Configure permissions and access rules +4. **Build Workflows**: Automate processes and approvals diff --git a/docs/spec/menu.md b/docs/spec/menu.md deleted file mode 100644 index 6c88a1c5..00000000 --- a/docs/spec/menu.md +++ /dev/null @@ -1,771 +0,0 @@ -# Menu & Navigation Metadata - -Menu and navigation metadata defines the application structure, navigation hierarchy, and user interface organization. - -## 1. Overview - -Navigation features include: - -- **Menu Structures**: Top nav, side nav, context menus -- **Hierarchical Organization**: Nested menu items and folders -- **Dynamic Menus**: Role-based, permission-aware navigation -- **Quick Actions**: Global actions and shortcuts -- **Breadcrumbs**: Automatic navigation trails -- **Search Integration**: Global search within navigation - -**File Naming Convention:** `.menu.yml` - -The filename (without the `.menu.yml` extension) automatically becomes the menu's identifier. - -**Examples:** -- `main_navigation.menu.yml` → Menu name: `main_navigation` -- `admin_menu.menu.yml` → Menu name: `admin_menu` - -**Note:** Menu metadata is often embedded within Application metadata (`.app.yml` files) as the `navigation` property. See [Application Metadata](./application.md) for the recommended approach. - -## 2. Root Structure - -```yaml -# File: main_navigation.menu.yml -# Menu name is inferred from filename! - -label: Main Menu -type: sidebar # sidebar, topnav, context, mobile -app: sales # Optional: Link to a specific application - -# Menu Items -items: - # Dashboard - - name: home - label: Home - icon: home - path: / - type: page - - # Sales Section - - name: sales - label: Sales - icon: currency - type: section - items: - - name: leads - label: Leads - icon: users - path: /sales/leads - object: leads - view: list - - - name: opportunities - label: Opportunities - icon: funnel - path: /sales/opportunities - object: opportunities - view: kanban - - - name: quotes - label: Quotes - icon: document - path: /sales/quotes - object: quotes - - # Service Section - - name: service - label: Service - icon: support - type: section - items: - - name: cases - label: Cases - path: /service/cases - object: cases - badge: - query: - object: cases - filters: [['status', '=', 'open']] - function: count - color: red - - - name: knowledge - label: Knowledge Base - path: /service/knowledge - object: articles - - # Reports - - name: reports - label: Reports - icon: chart - type: section - items: - - name: sales_reports - label: Sales Reports - type: folder - items: - - name: revenue_report - label: Revenue Report - path: /reports/revenue - icon: report - - - name: pipeline_report - label: Pipeline Report - path: /reports/pipeline - icon: report - - - name: dashboards - label: Dashboards - type: folder - items: - - name: sales_dashboard - label: Sales Dashboard - path: /dashboards/sales - icon: dashboard - - # Divider - - type: divider - - # Settings - - name: settings - label: Settings - icon: settings - path: /settings - permissions: - - admin - -# Quick Actions (Global) -quick_actions: - - name: new_lead - label: New Lead - icon: add - action: create_record - object: leads - hotkey: Ctrl+Shift+L - - - name: new_case - label: New Case - icon: add - action: create_record - object: cases - hotkey: Ctrl+Shift+C - - - name: search - label: Global Search - icon: search - action: open_search - hotkey: Ctrl+K -``` - -## 3. Menu Types - -| Type | Description | Use Case | -|:---|:---|:---| -| `sidebar` | Vertical side navigation | Main app navigation | -| `topnav` | Horizontal top bar | Secondary navigation | -| `context` | Context-sensitive menu | Right-click, actions menu | -| `mobile` | Mobile hamburger menu | Mobile navigation | -| `breadcrumb` | Navigation trail | Page hierarchy | - -## 4. Menu Item Types - -### 4.1 Page Links - -```yaml -items: - - name: dashboard - label: Dashboard - type: page - path: /dashboard - icon: home - - # Open behavior - target: _self # _self, _blank, modal - - # Highlight when active - active_match: /dashboard* -``` - -### 4.2 Object Views - -Direct links to object list views: - -```yaml -items: - - name: accounts - label: Accounts - type: object - object: accounts - view: list # Uses default or specified view - icon: company - path: /accounts - - # Quick filters - default_filters: - - field: status - operator: "=" - value: active -``` - -### 4.3 Sections/Folders - -Group related items: - -```yaml -items: - - name: sales_section - label: Sales - type: section # or 'folder' - icon: currency - - # Collapsible - collapsible: true - collapsed: false - - # Nested items - items: - - name: leads - label: Leads - path: /leads - - - name: accounts - label: Accounts - path: /accounts -``` - -### 4.4 Actions - -Execute actions from menu: - -```yaml -items: - - name: export_data - label: Export Data - type: action - icon: download - action: export_records - - # Confirmation - confirm: Export all data? - - # Params - params: - format: csv -``` - -### 4.5 External Links - -Link to external resources: - -```yaml -items: - - name: help_center - label: Help Center - type: external - url: https://help.example.com - icon: help - target: _blank -``` - -### 4.6 Dividers - -Visual separators: - -```yaml -items: - - type: divider - - # Or with label - - type: divider - label: Admin Tools -``` - -## 5. Dynamic Menu Items - -Menu items that adapt based on context: - -```yaml -items: - # Show only if user has permission - - name: admin_panel - label: Admin Panel - path: /admin - icon: shield - - permissions: - - admin - - # Show based on feature flag - - name: beta_feature - label: Beta Feature - path: /beta - - visible_when: - feature_flag: beta_features_enabled - - # Show based on condition - - name: pending_approvals - label: Pending Approvals - path: /approvals - - visible_when: - query: - object: approvals - filters: [['status', '=', 'pending']] - function: count - operator: ">" - value: 0 -``` - -## 6. Badges & Indicators - -Show counts or status on menu items: - -```yaml -items: - - name: notifications - label: Notifications - path: /notifications - icon: bell - - # Badge configuration - badge: - # Dynamic count - query: - object: notifications - filters: - - field: read - operator: "=" - value: false - - field: user_id - operator: "=" - value: $current_user.id - function: count - - # Styling - color: red # red, blue, green, yellow, gray - max_value: 99 # Show "99+" if exceeds - show_zero: false -``` - -## 7. Favorites & Pinned Items - -User-customizable favorites: - -```yaml -favorites: - enabled: true - - # Default favorites - default_items: - - dashboard - - my_tasks - - my_calendar - - # Max favorites - max_favorites: 10 - - # Position - position: top # top, bottom, section - - # Label - section_label: Favorites -``` - -## 8. Recent Items - -Track recently accessed items: - -```yaml -recent_items: - enabled: true - - # Number to track - max_items: 10 - - # Include types - include_types: - - page - - object - - report - - # Position in menu - position: top - section_label: Recent -``` - -## 9. Search Integration - -Global search in navigation: - -```yaml -search: - enabled: true - - # Position - position: top - - # Hotkey - hotkey: Ctrl+K - - # Search scope - scope: - - objects - - reports - - dashboards - - help_articles - - # Quick actions in search - quick_actions: - - label: New Lead - action: create_record - object: leads - - - label: New Task - action: create_record - object: tasks -``` - -## 10. Breadcrumbs - -Automatic navigation trail: - -```yaml -breadcrumbs: - enabled: true - - # Show on pages - show_on: - - object_detail - - object_edit - - reports - - # Format - separator: "/" - - # Home link - include_home: true - home_label: Home - - # Truncation - max_items: 5 - truncate_middle: true -``` - -## 11. Multi-Level Navigation - -Complex nested structures: - -```yaml -items: - - name: sales - label: Sales - type: section - icon: currency - - items: - # Level 2 - - name: pipeline - label: Pipeline Management - type: folder - - items: - # Level 3 - - name: leads - label: Leads - path: /sales/pipeline/leads - - - name: opportunities - label: Opportunities - path: /sales/pipeline/opportunities - - # Level 2 - - name: accounts - label: Account Management - type: folder - - items: - # Level 3 - - name: all_accounts - label: All Accounts - path: /sales/accounts - - - name: my_accounts - label: My Accounts - path: /sales/accounts/mine -``` - -## 12. Responsive Navigation - -Mobile-optimized menus: - -```yaml -responsive: - # Mobile behavior - mobile: - type: drawer # drawer, tabs, bottom_nav - - # Collapsed by default - collapsed: true - - # Show icons only - compact: true - - # Bottom navigation - bottom_nav: - enabled: true - max_items: 5 - items: - - home - - tasks - - notifications - - profile - - # Tablet - tablet: - type: sidebar - collapsible: true - width: 240 -``` - -## 13. Context Menus - -Right-click and action menus: - -```yaml -# File: record_context_menu.menu.yml -# Menu name is inferred from filename! - -type: context -object: tasks - -items: - - name: edit - label: Edit - icon: edit - action: edit_record - - - name: delete - label: Delete - icon: delete - action: delete_record - confirm: Delete this task? - destructive: true - - - type: divider - - - name: duplicate - label: Duplicate - icon: copy - action: duplicate_record - - - name: share - label: Share - icon: share - action: share_record - - - type: divider - - - name: change_status - label: Change Status - icon: status - type: submenu - items: - - label: Mark Complete - action: update_field - field: status - value: completed - - - label: Mark In Progress - action: update_field - field: status - value: in_progress -``` - -## 14. Theme & Styling - -Customize menu appearance: - -```yaml -theme: - # Width - width: 280 - collapsed_width: 64 - - # Colors - background: "#1a1a1a" - text_color: "#ffffff" - active_background: "#2d2d2d" - active_text_color: "#4CAF50" - - # Icons - icon_size: 20 - show_icons: true - - # Typography - font_size: 14 - font_weight: 400 - - # Spacing - item_padding: 12 - section_padding: 20 - - # Animation - animation_duration: 200 - hover_effect: true -``` - -## 15. Keyboard Navigation - -Keyboard shortcuts and accessibility: - -```yaml -keyboard_navigation: - enabled: true - - # Shortcuts - shortcuts: - toggle_menu: Alt+M - search: Ctrl+K - next_item: ArrowDown - prev_item: ArrowUp - expand_section: ArrowRight - collapse_section: ArrowLeft - select: Enter - - # Accessibility - aria_labels: true - focus_visible: true - skip_navigation: true -``` - -## 16. User Customization - -Allow users to customize navigation: - -```yaml -user_customization: - # Reorder items - allow_reorder: true - - # Show/hide items - allow_toggle_visibility: true - - # Custom folders - allow_custom_folders: true - - # Themes - allow_theme_selection: true - - # Save preferences - save_preferences: true - per_user: true -``` - -## 17. Analytics & Tracking - -Track navigation usage: - -```yaml -analytics: - enabled: true - - # Track events - track: - - menu_item_click - - search_usage - - quick_action_usage - - # Metrics - metrics: - - popular_items - - user_navigation_paths - - search_queries -``` - -## 18. Implementation Example - -Complete menu definition: - -```yaml -# File: main_navigation.menu.yml -# Menu name is inferred from filename! - -label: Main Navigation -type: sidebar - -items: - - name: home - label: Home - icon: home - path: / - - - name: sales - label: Sales - icon: currency - type: section - items: - - name: leads - label: Leads - path: /leads - object: leads - - - name: accounts - label: Accounts - path: /accounts - object: accounts -``` - -**TypeScript Implementation:** - -```typescript -// File: main_navigation.menu.ts -import { MenuDefinition } from '@objectql/types'; - -export const main_menu: MenuDefinition = { - type: 'sidebar', - items: [ - { - name: 'home', - label: 'Home', - icon: 'home', - path: '/' - }, - { - name: 'sales', - label: 'Sales', - icon: 'currency', - type: 'section', - items: [ - { - name: 'leads', - label: 'Leads', - path: '/leads', - object: 'leads' - }, - { - name: 'accounts', - label: 'Accounts', - path: '/accounts', - object: 'accounts' - } - ] - } - ] -}; -``` - -## 19. Best Practices - -1. **Hierarchy**: Keep menu depth to 2-3 levels maximum -2. **Icons**: Use consistent, meaningful icons -3. **Labels**: Clear, concise menu labels -4. **Grouping**: Logically group related items -5. **Performance**: Lazy-load large menus -6. **Mobile**: Optimize for touch interfaces -7. **Accessibility**: Support keyboard navigation and screen readers -8. **Permissions**: Filter menu items by user permissions -9. **Testing**: Test navigation with different user roles - -## 20. Related Specifications - -- [Permissions](./permission.md) - Access control -- [Views](./view.md) - Page layouts -- [Objects](./object.md) - Data models -- [Actions](./action.md) - Menu actions diff --git a/docs/spec/metadata-standard.md b/docs/spec/metadata-standard.md index 1666a105..65abec8c 100644 --- a/docs/spec/metadata-standard.md +++ b/docs/spec/metadata-standard.md @@ -14,8 +14,6 @@ In ObjectQL, **metadata** is machine-readable configuration that describes: 2. **How to validate it** (Validation Rules, Constraints) 3. **Who can access it** (Permissions, Security) 4. **What business logic to execute** (Hooks, Actions, Workflows) -5. **How to present it** (Pages, Views, Forms, Reports) -6. **How to navigate it** (Menus, Dashboards) ## Complete Metadata Taxonomy @@ -200,179 +198,7 @@ steps: field: manager_id ``` -### 3. Presentation Layer - -#### [Pages](./page.md) -**Purpose**: Define composable UI pages with layouts, components, and interactions. - -**What you define**: -- Page layouts (dashboard, wizard, canvas, two-column) -- UI components (data grids, forms, charts, metrics) -- Data source bindings -- Component actions (navigate, submit, open modal) -- Responsive configurations -- Page-level permissions and state -- AI context for page understanding - -**Example**: -```yaml -name: dashboard -label: Project Dashboard -layout: dashboard - -components: - - id: total_projects - type: metric - label: Total Projects - data_source: - object: projects - query: { op: count } - grid: { x: 0, y: 0, w: 3, h: 2 } - - - id: tasks_grid - type: data_grid - data_source: - object: tasks - fields: [name, status, due_date] - sort: [[created_at, desc]] - grid: { x: 0, y: 2, w: 12, h: 6 } -``` - -#### [Views & Layouts](./view.md) -**Purpose**: Define how data is displayed to users. - -**What you define**: -- List views (tabular data) -- Grid views (inline editing) -- Kanban boards (drag & drop) -- Calendar views (events, timeline) -- Card layouts (mobile-friendly) -- Column configurations -- Default filters and sorting - -**Example** (`task_list.view.yml`): -```yaml -# File: task_list.view.yml -# View name is inferred from filename! - -type: list -object: tasks # Still specify which object to display -config: - columns: - - field: name - width: 300 - sortable: true - - field: status - renderer: badge - default_filters: - - field: status - operator: "!=" - value: completed -``` - -#### [Forms](./form.md) -**Purpose**: Define data entry and editing interfaces. - -**What you define**: -- Form layouts (sections, tabs, columns) -- Field configurations (labels, help text, defaults) -- Conditional logic (show/hide based on values) -- Validation rules -- Wizard forms (multi-step) -- Quick create forms - -**Example** (`project_form.form.yml`): -```yaml -# File: project_form.form.yml -# Form name is inferred from filename! - -type: edit -object: projects -layout: - sections: - - name: basic_info - label: Basic Information - columns: 2 - fields: - - name - - status - - owner -conditional_logic: - - condition: - field: status - operator: "=" - value: completed - actions: - - show_fields: [completion_date] -``` - -#### [Reports & Dashboards](./report.md) -**Purpose**: Analytics, visualization, and business intelligence. - -**What you define**: -- Tabular reports (data lists) -- Summary reports (grouped with totals) -- Matrix reports (pivot tables) -- Charts (bar, line, pie, etc.) -- Dashboards (KPIs, charts, metrics) -- Scheduled reports -- Export formats - -**Example** (`sales_by_region.report.yml`): -```yaml -# File: sales_by_region.report.yml -# Report name is inferred from filename! - -type: summary -object: orders -groupings: - - field: customer.region - label: Region -aggregations: - - function: sum - field: amount - label: Total Sales - - function: count - field: id - label: Order Count -chart: - enabled: true - type: bar -``` - -#### [Menus & Navigation](./menu.md) -**Purpose**: Define application structure and navigation. - -**What you define**: -- Menu hierarchies -- Navigation items -- Quick actions -- Breadcrumbs -- Favorites -- Search integration -- Role-based menu visibility - -**Example** (`sales_crm.app.yml`): -```yaml -name: main_navigation -type: sidebar -items: - - name: sales - label: Sales - icon: currency - type: section - items: - - name: leads - label: Leads - path: /sales/leads - object: leads - - name: opportunities - label: Opportunities - path: /sales/opportunities - object: opportunities -``` - -### 4. Security & Access Control +### 3. Security & Access Control #### [Permissions](./permission.md) **Purpose**: Control who can access what data and operations. @@ -451,21 +277,6 @@ src/ workflows/ # Business processes order_approval.workflow.yml customer_onboarding.workflow.yml - - views/ # Presentation - customer_list.view.yml - customer_kanban.view.yml - - forms/ # Data entry - customer_form.form.yml - quick_customer.form.yml - - reports/ # Analytics - sales_summary.report.yml - sales_dashboard.dashboard.yml - - navigation/ # App structure - main_menu.menu.yml ``` ## Development Workflow @@ -481,19 +292,13 @@ src/ 2. Create custom actions 3. Design workflows and approvals -### 3. UI Phase -1. Design views for different contexts -2. Create forms for data entry -3. Build reports and dashboards -4. Configure navigation menus - -### 4. Testing Phase +### 3. Testing Phase 1. Test with different user roles 2. Validate business rules 3. Check permission enforcement 4. Performance testing -### 5. Deployment +### 4. Deployment 1. Commit metadata to Git 2. Deploy to ObjectOS runtime 3. Monitor and iterate @@ -511,10 +316,7 @@ ObjectQL provides a universal loader and generic API for all metadata types. | Type | Extension | Identifier Source | Example | |---|---|---|---| | Object | `*.object.yml` | Filename = Object name | `project.object.yml` → object: `project` | -| View | `*.view.yml` | Filename = View name | `task_list.view.yml` → view: `task_list` | -| Form | `*.form.yml` | Filename = Form name | `project_form.form.yml` → form: `project_form` | | Application | `*.app.yml` | Filename = App name | `crm.app.yml` → app: `crm` | -| Report | `*.report.yml` | Filename = Report name | `sales_summary.report.yml` → report: `sales_summary` | | Workflow | `*.workflow.yml` | Filename = Workflow name | `approval.workflow.yml` → workflow: `approval` | | Permission | `*.permission.yml` | Filename = Object name | `project.permission.yml` → applies to: `project` | | Validation | `*.validation.yml` | Filename = Object name | `project.validation.yml` → applies to: `project` | @@ -530,9 +332,9 @@ ObjectQL provides a universal loader and generic API for all metadata types. All metadata types can be queried via the REST API: - `GET /api/metadata/:type` - - List all entries for a specific type (e.g. `/api/metadata/view`) + - List all entries for a specific type (e.g. `/api/metadata/object`) - `GET /api/metadata/:type/:id` - - Get the JSON content of a specific entry (e.g. `/api/metadata/view/task_list`) + - Get the JSON content of a specific entry (e.g. `/api/metadata/object/customer`) - `POST /api/metadata/:type/:id` - Update metadata content (if supported by storage) @@ -566,17 +368,9 @@ function CustomerList() { fields: name: { type: text } email: { type: email } - -# customer_list.view.yml -# View name comes from filename! -type: list -object: customer -columns: - - name - - email ``` -**Result**: ObjectOS automatically generates API, UI, validation, and security - all from metadata! +**Result**: ObjectOS automatically generates API, validation, and security - all from metadata! ## Benefits diff --git a/docs/spec/page.md b/docs/spec/page.md deleted file mode 100644 index e36f177f..00000000 --- a/docs/spec/page.md +++ /dev/null @@ -1,467 +0,0 @@ -# Page Specification - -## Overview - -Pages are the visual interface layer in ObjectQL applications. They define composable UI layouts that can render data from objects, display custom components, and orchestrate user interactions. Pages are defined using `*.page.yml` files and follow a declarative, component-based architecture. - -## File Convention - -``` -src/ - ├── dashboard.page.yml - ├── project_detail.page.yml - └── create_wizard.page.yml -``` - -## Schema - -### Root Structure - -```typescript -interface PageConfig { - // Identity - name: string; // Unique identifier - label: string; // Display name - description?: string; // Page description - icon?: string; // Icon identifier - - // Layout - layout: PageLayoutType; // Layout type - sections?: PageSection[]; // Page sections (for structured layouts) - components?: PageComponent[]; // Components (for simple layouts) - - // Data & Logic - data_sources?: Record; - actions?: Record; - - // Styling & Behavior - style?: ComponentStyle; - responsive?: ResponsiveConfig; - - // Access Control - permissions?: { - view?: string[]; - edit?: string[]; - }; - - // Metadata - meta?: { - title?: string; - description?: string; - keywords?: string[]; - }; - - // State Management - state?: { - initial?: Record; - persist?: boolean; - storage_key?: string; - }; - - // Features - realtime?: boolean; - refresh_interval?: number; - - // AI Context - ai_context?: { - intent?: string; - persona?: string; - tasks?: string[]; - }; -} -``` - -### Layout Types - -```typescript -type PageLayoutType = - | 'single_column' // Single vertical column - | 'two_column' // Left and right columns - | 'three_column' // Left, center, right - | 'dashboard' // Grid-based dashboard - | 'canvas' // Free-form positioning - | 'tabs' // Tab-based layout - | 'wizard' // Multi-step wizard - | 'custom'; // Custom layout -``` - -### Component Types - -```typescript -type PageComponentType = - // Data Display - | 'data_grid' // Table/grid - | 'detail_view' // Record details - | 'list' // List view - | 'chart' // Visualizations - | 'metric' // KPI display - | 'calendar' // Calendar view - | 'kanban' // Kanban board - | 'timeline' // Timeline/Gantt - - // Data Input - | 'form' // Data entry form - | 'button' // Action button - - // Layout - | 'container' // Group components - | 'tabs' // Tab container - | 'divider' // Visual separator - - // Content - | 'text' // Text/markdown - | 'html' // Custom HTML - | 'image' // Image display - | 'iframe' // Embedded content - - | 'custom'; // Custom component -``` - -### Component Structure - -```typescript -interface PageComponent { - id: string; // Unique component ID - type: PageComponentType; // Component type - label?: string; // Display label - description?: string; // Description - - // Data binding - data_source?: ComponentDataSource; - - // Component configuration - config?: Record; - - // Actions - actions?: { - on_click?: ComponentAction; - on_submit?: ComponentAction; - on_load?: ComponentAction; - on_change?: ComponentAction; - [key: string]: ComponentAction | undefined; - }; - - // Styling - style?: ComponentStyle; - responsive?: ResponsiveConfig; - - // Visibility & Access - visible_when?: Record; - permissions?: string[]; - - // Nested components - components?: PageComponent[]; - - // Grid positioning (for dashboard layout) - grid?: { - x: number; // Column (0-11) - y: number; // Row - w: number; // Width in grid units - h: number; // Height in grid units - }; - - // Custom component reference - component?: string; -} -``` - -### Data Sources - -```typescript -interface ComponentDataSource { - object?: string; // Object to query - filters?: any[]; // Filter conditions - fields?: string[]; // Fields to display - sort?: Array<[string, 'asc' | 'desc']>; - limit?: number; - paginate?: boolean; - expand?: Record; // Related objects - query?: any; // Custom query -} -``` - -### Actions - -```typescript -interface ComponentAction { - type: 'navigate' | 'open_modal' | 'run_action' | 'submit_form' | 'refresh' | 'custom'; - - // Navigation - path?: string; - - // Modal - modal?: string; - - // Action execution - action?: string; - object?: string; - - // Custom handler - handler?: string; - - // User feedback - confirm?: string; - success_message?: string; - on_error?: 'show_toast' | 'show_modal' | 'ignore'; -} -``` - -### Responsive Configuration - -```typescript -interface ResponsiveConfig { - mobile?: { - columns?: number; - visible?: boolean; - order?: number; - }; - tablet?: { - columns?: number; - visible?: boolean; - order?: number; - }; - desktop?: { - columns?: number; - visible?: boolean; - order?: number; - }; -} -``` - -## Examples - -### Dashboard Layout - -```yaml -name: dashboard -label: Project Dashboard -layout: dashboard - -components: - # KPI Metric - - id: total_projects - type: metric - label: Total Projects - data_source: - object: projects - query: - op: count - config: - format: number - icon: folder - color: blue - grid: - x: 0 - y: 0 - w: 3 - h: 2 - - # Chart - - id: status_chart - type: chart - label: Projects by Status - data_source: - object: projects - fields: ['status'] - query: - op: group_by - field: status - aggregate: count - config: - chart_type: pie - grid: - x: 3 - y: 0 - w: 6 - h: 4 - - # Data Grid - - id: tasks_grid - type: data_grid - label: Recent Tasks - data_source: - object: tasks - fields: ['name', 'status', 'due_date'] - sort: [['created_at', 'desc']] - limit: 10 - grid: - x: 0 - y: 2 - w: 12 - h: 6 - -permissions: - view: ['admin', 'manager', 'user'] -``` - -### Two-Column Detail Page - -```yaml -name: project_detail -label: Project Details -layout: two_column - -sections: - # Main content - - id: main_content - type: content - style: - width: 70% - components: - - id: edit_form - type: form - label: Project Information - data_source: - object: projects - query: - op: findOne - filter: [['_id', '=', '{{route.params.id}}']] - config: - mode: edit - fields: - - name: name - label: Name - type: text - - name: description - label: Description - type: textarea - actions: - on_submit: - type: run_action - object: projects - action: update - - # Sidebar - - id: sidebar - type: sidebar - style: - width: 30% - components: - - id: stats - type: metric - label: Task Count - data_source: - object: tasks - filters: - - ['project', '=', '{{route.params.id}}'] - query: - op: count -``` - -### Wizard Layout - -```yaml -name: create_project -label: Create Project -layout: wizard - -components: - # Step 1 - - id: step_basic - type: container - label: Basic Information - config: - step: 1 - components: - - id: basic_form - type: form - config: - fields: - - name: name - label: Name - type: text - required: true - - # Step 2 - - id: step_team - type: container - label: Team - config: - step: 2 - components: - - id: team_form - type: form - config: - fields: - - name: owner - label: Owner - type: lookup - reference_to: users - -actions: - submit_wizard: - type: run_action - object: projects - action: create - success_message: Project created! -``` - -## Usage in Applications - -Pages can be referenced in application navigation: - -```yaml -# app.yml -navigation: - type: sidebar - items: - - type: page - name: dashboard - label: Dashboard - icon: dashboard - path: /dashboard - - - type: section - label: Projects - items: - - type: page - name: project_list - path: /projects -``` - -## Best Practices - -1. **Component IDs**: Use descriptive IDs (e.g., `tasks_grid` not `grid1`) -2. **Data Binding**: Leverage `{{}}` syntax for dynamic values -3. **Responsive Design**: Always configure responsive behavior -4. **Access Control**: Define clear permissions -5. **AI Context**: Provide intent and tasks for AI understanding -6. **State Management**: Use page state for complex interactions -7. **Performance**: Use pagination and limits for large datasets - -## Validation Rules - -- `name` must be unique within the application -- `layout` is required -- Either `sections` or `components` must be defined (not both for simple layouts) -- Component `id` must be unique within the page -- Grid positions must not overlap in dashboard layouts -- Responsive breakpoints must be valid - -## File Loading - -Pages are automatically loaded from `*.page.yml` files by the ObjectQL loader: - -```typescript -// Automatically registered -loader.load('./src'); - -// Access via registry -const page = registry.get('page', 'dashboard'); -``` - -## Integration with Studio - -The ObjectQL Studio provides a visual interface for: -- Browsing registered pages -- Previewing page layouts -- Editing page metadata -- Testing responsive behavior -- Managing permissions - -## See Also - -- [Application Configuration](./application.md) -- [Form Specification](./form.md) -- [View Specification](./view.md) -- [Component Library](../guide/components.md) diff --git a/docs/spec/report.md b/docs/spec/report.md deleted file mode 100644 index 73146925..00000000 --- a/docs/spec/report.md +++ /dev/null @@ -1,789 +0,0 @@ -# Report & Dashboard Metadata - -Report and dashboard metadata defines data visualization, analytics, and reporting capabilities. This enables business intelligence directly from ObjectQL metadata. - -## 1. Overview - -Reporting features include: - -- **Multiple Report Types**: Tabular, summary, matrix (pivot), chart -- **Multi-Object Joins**: Query across related objects -- **Grouping & Aggregations**: COUNT, SUM, AVG, MIN, MAX -- **Filtering**: Dynamic filters and criteria -- **Scheduling**: Automated report generation and delivery -- **Export**: PDF, Excel, CSV formats -- **Dashboards**: Combine multiple reports and charts - -**File Naming Convention:** `.report.yml`, `.dashboard.yml` - -The filename (without the `.report.yml` or `.dashboard.yml` extension) automatically becomes the report/dashboard identifier. - -**Examples:** -- `sales_summary.report.yml` → Report name: `sales_summary` -- `executive_dashboard.dashboard.yml` → Dashboard name: `executive_dashboard` - -## 2. Report Types - -| Type | Description | Use Case | -|:---|:---|:---| -| `tabular` | Simple list/table | Flat data lists, exports | -| `summary` | Grouped with subtotals | Sales by region, tasks by status | -| `matrix` | Pivot table/cross-tab | Time-series analysis, comparisons | -| `chart` | Visualization only | Dashboards, presentations | - -## 3. Tabular Reports - -Simple list reports with columns and filters: - -```yaml -# File: open_tasks.report.yml -# Report name is inferred from filename! - -label: Open Tasks -type: tabular -object: tasks - -# Columns -columns: - - field: name - label: Task Name - width: 300 - - - field: assignee.name - label: Assigned To - width: 150 - - - field: priority - label: Priority - width: 100 - - - field: due_date - label: Due Date - width: 120 - format: MM/DD/YYYY - - - field: project.name - label: Project - width: 200 - -# Filters -filters: - - field: status - operator: "!=" - value: completed - - - field: due_date - operator: ">=" - value: $today - -# Sorting -sort: - - field: priority - direction: desc - - field: due_date - direction: asc - -# Limit -limit: 1000 - -# Allow export -export: - enabled: true - formats: [csv, excel, pdf] -``` - -## 4. Summary Reports - -Grouped reports with aggregations: - -```yaml -name: sales_by_region -label: Sales Summary by Region -type: summary -object: orders - -# Grouping Levels -groupings: - # Primary grouping - - field: customer.region - label: Region - sort: asc - - # Secondary grouping - - field: product_category - label: Category - sort: asc - -# Detail Columns -columns: - - field: order_number - label: Order # - - - field: customer.name - label: Customer - - - field: order_date - label: Date - format: MM/DD/YYYY - - - field: amount - label: Amount - format: currency - -# Aggregations -aggregations: - # Count of records - - function: count - field: id - label: Total Orders - display_at: [group, total] - - # Sum - - function: sum - field: amount - label: Total Sales - format: currency - display_at: [group, total] - - # Average - - function: avg - field: amount - label: Average Order Value - format: currency - display_at: [group, total] - - # Min/Max - - function: min - field: order_date - label: First Order - format: date - display_at: [group] - - - function: max - field: order_date - label: Last Order - format: date - display_at: [group] - -# Filters -filters: - - field: status - operator: "=" - value: completed - - - field: order_date - operator: between - value: [$start_of_year, $end_of_year] - -# Chart -chart: - enabled: true - type: bar - x_axis: customer.region - y_axis: amount - aggregation: sum -``` - -## 5. Matrix Reports - -Pivot table / cross-tab reports: - -```yaml -name: sales_by_month_and_product -label: Sales Matrix -type: matrix -object: orders - -# Row Grouping -row_groupings: - - field: product.category - label: Product Category - sort: asc - - - field: product.name - label: Product - sort: asc - -# Column Grouping -column_groupings: - - field: order_date - label: Month - date_function: month # year, quarter, month, week, day - format: MMM YYYY - -# Measure (Value in cells) -measure: - function: sum - field: amount - label: Total Sales - format: currency - -# Show Totals -totals: - row_totals: true - column_totals: true - grand_total: true - -# Filters -filters: - - field: order_date - operator: ">=" - value: $start_of_year - - - field: status - operator: "=" - value: completed - -# Chart -chart: - enabled: true - type: stacked_bar -``` - -## 6. Chart Reports - -Visualization-focused reports: - -```yaml -name: revenue_trend -label: Revenue Trend -type: chart -object: orders - -# Chart Configuration -chart: - type: line # line, bar, pie, donut, area, scatter - title: Monthly Revenue Trend - - # Data - x_axis: - field: order_date - label: Month - date_function: month - format: MMM YYYY - - y_axis: - function: sum - field: amount - label: Revenue - format: currency - - # Multiple series - series: - field: product_category - label: Category - - # Styling - height: 400 - show_legend: true - show_grid: true - show_data_labels: false - - # Colors - color_scheme: default # default, blue, green, red, custom - - # Interactive - interactive: true - allow_drill_down: true - -# Filters -filters: - - field: order_date - operator: ">=" - value: $start_of_year - - - field: status - operator: "=" - value: completed -``` - -### 6.1 Chart Types - -```yaml -# Bar Chart -chart: - type: bar - orientation: vertical # vertical, horizontal - stacked: false - -# Line Chart -chart: - type: line - smooth: true - fill_area: false - -# Pie/Donut Chart -chart: - type: pie # or donut - show_percentage: true - show_labels: true - -# Area Chart -chart: - type: area - stacked: true - -# Scatter Plot -chart: - type: scatter - x_axis: { field: quantity } - y_axis: { field: price } - size_by: { field: discount } - -# Combo Chart -chart: - type: combo - series: - - type: bar - field: revenue - y_axis: primary - - type: line - field: profit_margin - y_axis: secondary -``` - -## 7. Dashboard Configuration - -Combine multiple reports and charts: - -```yaml -name: sales_dashboard -label: Sales Dashboard -description: Executive sales overview - -# Layout -layout: - columns: 12 # Grid system - - # Widgets - widgets: - # KPI Cards - - type: metric - title: Total Revenue - position: { row: 1, col: 1, width: 3, height: 2 } - metric: - object: orders - function: sum - field: amount - format: currency - filters: - - field: status - value: completed - - # Comparison - compare_to: previous_month - show_trend: true - - - type: metric - title: Active Customers - position: { row: 1, col: 4, width: 3, height: 2 } - metric: - object: customers - function: count - field: id - filters: - - field: status - value: active - - - type: metric - title: Avg Order Value - position: { row: 1, col: 7, width: 3, height: 2 } - metric: - object: orders - function: avg - field: amount - format: currency - - - type: metric - title: Conversion Rate - position: { row: 1, col: 10, width: 3, height: 2 } - metric: - formula: (completed_orders / total_leads) * 100 - format: percentage - - # Chart Widget - - type: chart - title: Revenue Trend - position: { row: 3, col: 1, width: 8, height: 4 } - report: revenue_trend - - # Table Widget - - type: table - title: Top Products - position: { row: 3, col: 9, width: 4, height: 4 } - report: top_products - limit: 10 - - # Chart Widget 2 - - type: chart - title: Sales by Region - position: { row: 7, col: 1, width: 6, height: 4 } - report: sales_by_region - - # Chart Widget 3 - - type: chart - title: Product Mix - position: { row: 7, col: 7, width: 6, height: 4 } - chart: - type: pie - object: order_items - measure: { function: sum, field: quantity } - grouping: product_category - -# Filters (apply to all widgets) -filters: - - name: date_range - label: Date Range - type: date_range - default: this_month - - - name: region - label: Region - type: select - object: regions - field: name - allow_all: true - -# Refresh -refresh: - auto_refresh: true - interval: 300 # 5 minutes - -# Permissions -permissions: - view: [admin, manager, sales] - edit: [admin] -``` - -## 8. Report Filters - -### 8.1 Standard Filters - -```yaml -filters: - # Equality - - field: status - operator: "=" - value: active - - # Comparison - - field: amount - operator: ">" - value: 1000 - - # Range - - field: created_date - operator: between - value: [2024-01-01, 2024-12-31] - - # List - - field: category - operator: in - value: [electronics, computers, phones] - - # String matching - - field: name - operator: contains - value: laptop - - # Null check - - field: deleted_date - operator: is_null -``` - -### 8.2 Dynamic Filters - -```yaml -filters: - # Current user - - field: owner_id - operator: "=" - value: $current_user.id - - # Date functions - - field: created_date - operator: "=" - value: $today - - # Relative dates - - field: created_date - operator: ">=" - value: $start_of_month - - # Formulas - - field: total - operator: ">" - value: $average_order_value * 2 -``` - -### 8.3 User Filters - -Allow users to customize filters: - -```yaml -user_filters: - - name: date_range - label: Date Range - field: order_date - type: date_range - default: this_month - required: false - - - name: status - label: Status - field: status - type: multi_select - options: - - draft - - pending - - completed - default: [pending, completed] - - - name: region - label: Region - field: customer.region - type: lookup - object: regions - allow_all: true - - - name: amount_min - label: Min Amount - field: amount - type: number - operator: ">=" -``` - -## 9. Formulas & Calculated Fields - -Create computed values: - -```yaml -columns: - # Simple calculation - - name: profit - label: Profit - formula: revenue - cost - format: currency - - # Percentage - - name: profit_margin - label: Profit Margin - formula: (revenue - cost) / revenue * 100 - format: percentage - - # Conditional - - name: status_category - label: Category - formula: | - if (amount > 10000) { - return 'High Value'; - } else if (amount > 1000) { - return 'Medium Value'; - } else { - return 'Low Value'; - } - - # Date calculations - - name: days_open - label: Days Open - formula: $today - created_date - format: number - - # Lookup - - name: account_owner_name - label: Account Owner - formula: customer.account.owner.name -``` - -## 10. Scheduling & Distribution - -Automate report generation: - -```yaml -schedule: - # Enable scheduling - enabled: true - - # Frequency - frequency: weekly # daily, weekly, monthly, custom - - # Custom schedule - cron: "0 9 * * 1" # Every Monday at 9 AM - - # Time zone - timezone: America/New_York - - # Distribution - recipients: - - email: sales-team@company.com - - user_id: manager_123 - - role: executive - - # Email settings - email: - subject: Weekly Sales Report - ${date} - body: | - Please find attached the weekly sales report. - - Report Period: ${start_date} to ${end_date} - - # Attachment format - format: pdf # pdf, excel, csv - - # Inline preview - include_preview: true - - # Filters for scheduled run - filter_values: - date_range: last_week - region: all -``` - -## 11. Export Configuration - -```yaml -export: - enabled: true - - # Available formats - formats: - - csv - - excel - - pdf - - # Excel options - excel: - sheet_name: Sales Report - include_filters: true - include_chart: true - freeze_header: true - - # PDF options - pdf: - page_size: letter # letter, a4, legal - orientation: portrait # portrait, landscape - include_logo: true - header: Company Sales Report - footer: "Page ${page} of ${total_pages}" - - # CSV options - csv: - delimiter: "," - include_header: true - encoding: utf-8 -``` - -## 12. Performance Optimization - -```yaml -performance: - # Caching - cache: - enabled: true - ttl: 3600 # 1 hour - - # Cache key includes - cache_key_fields: - - filters - - user_id - - # Pagination - pagination: - enabled: true - page_size: 100 - max_records: 10000 - - # Indexing hints - use_indexes: - - customer.region - - order_date - - # Query optimization - optimize: - push_down_filters: true - parallel_execution: true -``` - -## 13. Access Control - -```yaml -permissions: - # Who can view - view: - - admin - - manager - - sales_rep - - # Who can edit report definition - edit: - - admin - - # Row-level security - row_level_security: - enabled: true - filter: - # Users only see their own data - field: owner_id - operator: "=" - value: $current_user.id - - # Except for admins - bypass_roles: - - admin - - manager - - # Field-level security - field_restrictions: - cost: - visible_to: [admin, finance] - - profit_margin: - visible_to: [admin, executive] -``` - -## 14. Implementation Example - -```typescript -// src/reports/sales_summary.report.yml -import { ReportDefinition } from '@objectql/types'; - -export const sales_summary: ReportDefinition = { - name: 'sales_summary', - type: 'summary', - object: 'orders', - groupings: [ - { field: 'customer.region', label: 'Region' } - ], - aggregations: [ - { function: 'sum', field: 'amount', label: 'Total Sales' }, - { function: 'count', field: 'id', label: 'Order Count' } - ], - filters: [ - ['status', '=', 'completed'] - ], - chart: { - enabled: true, - type: 'bar', - x_axis: 'customer.region', - y_axis: 'amount' - } -}; -``` - -## 15. Best Practices - -1. **Performance**: Use indexes, limit record counts -2. **Filters**: Provide sensible default filters -3. **Aggregations**: Use appropriate summary functions -4. **Formatting**: Apply proper number/date formatting -5. **Permissions**: Implement row-level security -6. **Caching**: Enable caching for frequently-run reports -7. **Naming**: Use clear, descriptive names -8. **Documentation**: Document report purpose and filters - -## 16. Related Specifications - -- [Objects & Fields](./object.md) - Data models -- [Query Language](./query-language.md) - Query syntax -- [Views](./view.md) - Display layouts -- [Permissions](./permission.md) - Access control diff --git a/docs/spec/view.md b/docs/spec/view.md deleted file mode 100644 index 4f899bd4..00000000 --- a/docs/spec/view.md +++ /dev/null @@ -1,416 +0,0 @@ -# View & Layout Metadata - -Views define how data is presented to users in different contexts (list, detail, grid, kanban, calendar, etc.). They are the UI presentation layer of ObjectQL metadata. - -## 1. Overview - -View metadata separates the presentation logic from the data model, allowing the same object to be displayed differently based on context, user role, or device type. - -**File Naming Convention:** `.view.yml` - -The filename (without the `.view.yml` extension) automatically becomes the view's identifier. This eliminates the need for a redundant `name` property. - -**Examples:** -- `task_list.view.yml` → View name: `task_list` -- `customer_kanban.view.yml` → View name: `customer_kanban` -- `project_calendar.view.yml` → View name: `project_calendar` - -## 2. View Types - -ObjectQL supports multiple view types for different use cases: - -| View Type | Description | Use Case | -|:---|:---|:---| -| `list` | Tabular list of records | Default view for browsing data | -| `grid` | Data grid with inline editing | Power user data entry | -| `kanban` | Kanban board grouped by field | Task/project management | -| `calendar` | Calendar view with events | Scheduling, timeline views | -| `timeline` | Gantt-style timeline | Project planning | -| `card` | Card-based layout | Mobile-friendly browsing | -| `detail` | Single record detail view | Record inspection | -| `form` | Editable form layout | Data entry/editing | - -## 3. Root Properties - -```yaml -# File: task_list.view.yml -# View name is inferred from filename! - -label: Task List -type: list -object: tasks # Must specify which object to display -description: Default task listing view - -# View Configuration -config: - columns: [...] - filters: [...] - sort: [...] - actions: [...] -``` - -| Property | Type | Required | Description | -|:---|:---|:---|:---| -| `label` | `string` | ✓ | Display name for the view | -| `type` | `ViewType` | ✓ | Type of view (list, grid, kanban, etc.) | -| `object` | `string` | ✓ | Target object name | -| `description` | `string` | | Help text or purpose | -| `icon` | `string` | | Icon identifier | -| `default` | `boolean` | | Whether this is the default view for the object | -| `config` | `object` | ✓ | View-specific configuration | - -**Note:** The `name` property is **no longer needed** - it's automatically inferred from the filename. - -## 4. List View Configuration - -The most common view type for browsing records. - -```yaml -# File: task_list.view.yml - -type: list -object: tasks -config: - # Column Definitions - columns: - - field: name - label: Task Name - width: 300 - sortable: true - searchable: true - - - field: status - label: Status - width: 120 - renderer: badge - - - field: priority - label: Priority - width: 100 - renderer: priority_indicator - - - field: assignee.name - label: Assigned To - width: 150 - - - field: due_date - label: Due Date - width: 120 - renderer: date - format: MM/DD/YYYY - - # Default Filters - default_filters: - - field: status - operator: "!=" - value: completed - - # Default Sort Order - default_sort: - - field: priority - direction: desc - - field: due_date - direction: asc - - # Pagination - page_size: 50 - - # Available Actions - row_actions: - - complete_task - - edit - - delete - - bulk_actions: - - bulk_assign - - bulk_delete -``` - -### 4.1 Column Configuration - -| Property | Type | Description | -|:---|:---|:---| -| `field` | `string` | Field path (supports dot notation for related fields) | -| `label` | `string` | Column header text | -| `width` | `number` | Column width in pixels | -| `sortable` | `boolean` | Enable column sorting | -| `searchable` | `boolean` | Include in search filter | -| `renderer` | `string` | Custom renderer type (badge, link, date, etc.) | -| `format` | `string` | Format string for dates/numbers | -| `hidden` | `boolean` | Hide by default (user can unhide) | - -## 5. Kanban View Configuration - -Kanban views group records by a specific field value. - -```yaml -name: task_kanban -type: kanban -object: tasks -config: - # Grouping Field - group_by: status - - # Card Configuration - card: - title_field: name - subtitle_field: assignee.name - description_field: description - image_field: cover_image - - # Additional fields to display on card - fields: - - field: priority - renderer: badge - - field: due_date - renderer: date_badge - - # Card Actions - actions: - - edit - - delete - - # Column Configuration - columns: - - value: backlog - label: Backlog - color: gray - - - value: in_progress - label: In Progress - color: blue - limit: 5 # WIP limit - - - value: done - label: Done - color: green - - # Enable drag & drop between columns - draggable: true - - # Filter - filters: - - field: archived - operator: "=" - value: false -``` - -## 6. Calendar View Configuration - -Calendar views display records as events on a timeline. - -```yaml -name: event_calendar -type: calendar -object: events -config: - # Date Field Mapping - start_date_field: start_time - end_date_field: end_time - all_day_field: is_all_day - - # Event Display - title_field: name - color_field: event_type - - # Color Mapping - color_mapping: - meeting: blue - deadline: red - holiday: green - - # Default View - default_view: month # month, week, day, agenda - - # Allow creation via click - allow_create: true - - # Actions - event_actions: - - edit - - delete - - duplicate -``` - -## 7. Detail View Configuration - -Detail views show a single record with full field display. - -```yaml -name: project_detail -type: detail -object: projects -config: - # Layout Sections - sections: - - name: overview - label: Project Overview - columns: 2 - fields: - - name - - status - - owner - - start_date - - end_date - - budget - - - name: description - label: Description - columns: 1 - fields: - - description - - objectives - - - name: team - label: Team Members - type: related_list - relation: team_members - columns: - - member.name - - role - - hours_allocated - - # Related Lists (child objects) - related_lists: - - object: tasks - relation_field: project_id - label: Project Tasks - default_view: task_list - allow_create: true - - - object: files - relation_field: project_id - label: Attachments - view_type: grid -``` - -## 8. Responsive Design - -Views can define responsive breakpoints for mobile/tablet optimization. - -```yaml -config: - responsive: - mobile: - # Show fewer columns on mobile - columns: - - name - - status - - # Use card layout instead of table - layout: card - - tablet: - columns: - - name - - status - - assignee - - due_date -``` - -## 9. Conditional Rendering - -Show/hide elements based on conditions. - -```yaml -config: - columns: - - field: internal_notes - label: Internal Notes - # Only show to admins - visible_if: - user_role: admin - - - field: approval_status - label: Approval - # Only show if amount > 1000 - visible_if: - field: amount - operator: ">" - value: 1000 -``` - -## 10. Custom Renderers - -Define custom rendering logic for specific field types. - -```yaml -config: - renderers: - priority_indicator: - type: component - component: PriorityBadge - props: - showIcon: true - - status_badge: - type: badge - color_mapping: - draft: gray - pending: yellow - approved: green - rejected: red -``` - -## 11. View Permissions - -Control who can see and use specific views. - -```yaml -permissions: - # Who can see this view - read: - - admin - - manager - - user - - # Who can modify view settings - modify: - - admin - - # Field-level visibility - field_permissions: - salary: - visible_to: [admin, hr_manager] -``` - -## 12. Implementation Example - -```typescript -// src/views/task_list.view.yml -import { ViewDefinition } from '@objectql/types'; - -export const task_list: ViewDefinition = { - name: 'task_list', - type: 'list', - object: 'tasks', - config: { - columns: [ - { field: 'name', label: 'Task', width: 300 }, - { field: 'status', label: 'Status', renderer: 'badge' } - ], - default_filters: [ - ['status', '!=', 'completed'] - ], - default_sort: [ - ['priority', 'desc'] - ] - } -}; -``` - -## 13. Best Practices - -1. **Start Simple**: Begin with list and detail views, add specialized views as needed -2. **Reusable Renderers**: Create custom renderers for commonly used patterns -3. **Mobile First**: Always define responsive behavior for mobile devices -4. **Performance**: Limit initial column count for large datasets -5. **User Customization**: Allow users to save personal view preferences -6. **Accessibility**: Ensure proper ARIA labels and keyboard navigation - -## 14. Related Specifications - -- [Objects & Fields](./object.md) - Data model definition -- [Forms](./form.md) - Editable form layouts -- [Permissions](./permission.md) - Access control -- [Actions](./action.md) - Available operations diff --git a/examples/browser/README.md b/examples/browser/README.md deleted file mode 100644 index df1b932f..00000000 --- a/examples/browser/README.md +++ /dev/null @@ -1,154 +0,0 @@ -# Browser Example for @objectql/sdk - -This example demonstrates how to use `@objectql/sdk` in different environments: - -- **`index.html`** - Pure browser example (no build tools) -- **`example-node.ts`** - Node.js/TypeScript example - -## 🚀 Quick Start - -### Prerequisites - -You need a running ObjectQL server. You can start one using: - -```bash -# From the repository root -cd examples/starters/hello-world -npm install -npm start -``` - -The server will be available at `http://localhost:3000`. - -### Running the Browser Example - -1. **Open the HTML file directly in a browser:** - -```bash -# Open in your default browser -open index.html # macOS -xdg-open index.html # Linux -start index.html # Windows -``` - -2. **Or serve it with a simple HTTP server:** - -```bash -# Using Python -python3 -m http.server 8080 - -# Using Node.js http-server -npx http-server -p 8080 - -# Using PHP -php -S localhost:8080 -``` - -Then navigate to `http://localhost:8080` in your browser. - -### Running the Node.js Example - -```bash -# Install dependencies (if not already installed) -cd ../../ -pnpm install - -# Run the example -cd examples/browser -npx ts-node example-node.ts -``` - -Or compile and run: - -```bash -npx tsc example-node.ts -node example-node.js -``` - -## 📋 Features Demonstrated - -The example shows how to: - -- ✅ Initialize `DataApiClient` and `MetadataApiClient` in the browser -- ✅ List records with filtering and pagination -- ✅ Get object metadata and schema -- ✅ Create new records -- ✅ Count records with filters -- ✅ Handle errors and loading states -- ✅ Works in all modern browsers (polyfill built-in!) - -## 🌐 Browser Compatibility - -This example works in all modern browsers: - -- Chrome 90+ -- Firefox 90+ -- Safari 15+ -- Edge 90+ - -**Note:** The `@objectql/sdk` package automatically includes a polyfill for `AbortSignal.timeout()`, so it works seamlessly in older browsers without any additional configuration! - -## 🔧 Using in Production - -For production applications, we recommend: - -### Option 1: Using a Module Bundler (Recommended) - -```bash -npm install @objectql/sdk @objectql/types -``` - -```javascript -import { DataApiClient, MetadataApiClient } from '@objectql/sdk'; - -const client = new DataApiClient({ - baseUrl: process.env.API_URL -}); -``` - -### Option 2: Using ES Modules via CDN - -```html - -``` - -### Option 3: Self-hosted ES Modules - -After building the package, you can serve the dist files: - -```html - -``` - -## 🔒 Security Considerations - -When using the SDK in the browser: - -1. **Never hardcode sensitive tokens** - Store them in environment variables or secure storage -2. **Use HTTPS** in production -3. **Implement CORS** properly on your ObjectQL server -4. **Validate all user input** before sending to the API -5. **Use Content Security Policy (CSP)** headers - -## 📚 Additional Resources - -- [ObjectQL SDK Documentation](../../packages/drivers/sdk/README.md) -- [Client SDK API Guide](../../docs/api/client-sdk.md) -- [REST API Reference](../../docs/api/rest.md) -- [ObjectQL Website](https://objectql.org) - -## 🤝 Contributing - -Found an issue or want to improve this example? Please open an issue or submit a PR! diff --git a/examples/browser/example-node.ts b/examples/browser/example-node.ts deleted file mode 100644 index 067846db..00000000 --- a/examples/browser/example-node.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Simple Node.js example using @objectql/sdk - * - * This demonstrates how to use the ObjectQL SDK in a Node.js environment. - * The same code works in browsers, Deno, and edge runtimes! - */ - -import { DataApiClient, MetadataApiClient } from '@objectql/sdk'; - -// Initialize clients -const dataClient = new DataApiClient({ - baseUrl: process.env.OBJECTQL_URL || 'http://localhost:3000', - token: process.env.OBJECTQL_TOKEN // Optional authentication -}); - -const metadataClient = new MetadataApiClient({ - baseUrl: process.env.OBJECTQL_URL || 'http://localhost:3000' -}); - -async function main() { - try { - console.log('🚀 ObjectQL SDK Node.js Example\n'); - - // 1. List all available objects - console.log('📚 Listing all objects...'); - const objectsResponse = await metadataClient.listObjects(); - console.log(`Found ${objectsResponse.items?.length || 0} objects:\n`); - objectsResponse.items?.forEach(obj => { - console.log(` - ${obj.name} (${obj.label})`); - }); - - // 2. Get schema for a specific object (e.g., 'users') - console.log('\n🔍 Getting schema for "users" object...'); - const userSchema = await metadataClient.getObject('users'); - console.log(`\nObject: ${userSchema.label}`); - console.log('Fields:'); - Object.entries(userSchema.fields).forEach(([key, field]) => { - console.log(` - ${field.name} (${field.type})${field.required ? ' *required' : ''}`); - }); - - // 3. List users with filtering - console.log('\n📋 Listing active users...'); - const usersResponse = await dataClient.list('users', { - filter: [['status', '=', 'active']], - sort: [['created_at', 'desc']], - limit: 5 - }); - console.log(`Found ${usersResponse.items?.length || 0} active users`); - usersResponse.items?.forEach(user => { - console.log(` - ${user.name} (${user.email})`); - }); - - // 4. Count total users - console.log('\n🔢 Counting all users...'); - const countResponse = await dataClient.count('users'); - console.log(`Total users: ${countResponse.count}`); - - // 5. Create a new user (example - commented out to avoid side effects) - /* - console.log('\n➕ Creating a new user...'); - const newUser = await dataClient.create('users', { - name: 'SDK Example User', - email: `sdk-example-${Date.now()}@example.com`, - status: 'active' - }); - console.log(`Created user: ${newUser.name} (ID: ${newUser._id})`); - */ - - console.log('\n✅ Example completed successfully!'); - } catch (error) { - console.error('\n❌ Error:', error.message); - - if (error.code) { - console.error('Error code:', error.code); - } - - if (error.details) { - console.error('Details:', error.details); - } - - process.exit(1); - } -} - -// Run the example -main(); diff --git a/examples/browser/index.html b/examples/browser/index.html deleted file mode 100644 index bb3bb58c..00000000 --- a/examples/browser/index.html +++ /dev/null @@ -1,519 +0,0 @@ - - - - - - ObjectQL SDK Browser Example - - - -
-
-

🚀 ObjectQL SDK Browser Example

-

Demonstrating @objectql/sdk in a pure browser environment

-
- -
-

⚙️ Configuration

-
- - -
-
- - -
-
- - -
-
- -
-

📊 Actions

-
- - - - -
- -
- - -
- -
- - -
- -
- -
- -
- - -
- -

📄 Results

-
-

Click a button above to perform an action...

-
-
-
- - - - diff --git a/package.json b/package.json index a1c288ba..a362b2a2 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "build": "tsc -b && pnpm -r run build", "check-versions": "node scripts/check-versions.js", "test": "pnpm run check-versions && pnpm -r run test", - "studio": "node packages/tools/cli/dist/index.js studio", "ai": "node packages/tools/cli/dist/index.js ai", "changeset": "changeset", "version": "changeset version", diff --git a/packages/foundation/core/src/ai-agent.ts b/packages/foundation/core/src/ai-agent.ts index 73dc1f46..fb876829 100644 --- a/packages/foundation/core/src/ai-agent.ts +++ b/packages/foundation/core/src/ai-agent.ts @@ -51,7 +51,7 @@ export interface GenerateAppResult { files: Array<{ filename: string; content: string; - type: 'object' | 'validation' | 'form' | 'view' | 'page' | 'menu' | 'action' | 'hook' | 'permission' | 'workflow' | 'report' | 'data' | 'application' | 'typescript' | 'test' | 'other'; + type: 'object' | 'validation' | 'action' | 'hook' | 'permission' | 'workflow' | 'data' | 'application' | 'typescript' | 'test' | 'other'; }>; /** Any errors encountered */ errors?: string[]; @@ -464,8 +464,7 @@ export class ObjectQLAgent { const fileTypes = new Set(currentFiles.map(f => f.type)); const allTypes = [ - 'object', 'validation', 'form', 'view', 'page', - 'menu', 'action', 'hook', 'permission', 'workflow', 'report', 'data' + 'object', 'validation', 'action', 'hook', 'permission', 'workflow', 'data' ]; const missingTypes = allTypes.filter(t => !fileTypes.has(t as any)); @@ -478,17 +477,9 @@ export class ObjectQLAgent { suggestions.push('Add permissions to control access'); } - if (!fileTypes.has('menu')) { - suggestions.push('Create a menu for navigation'); - } - if (!fileTypes.has('workflow') && fileTypes.has('object')) { suggestions.push('Add workflows for approval processes'); } - - if (!fileTypes.has('report') && fileTypes.has('object')) { - suggestions.push('Generate reports for analytics'); - } return suggestions; } @@ -512,14 +503,7 @@ Follow ObjectQL metadata standards for ALL metadata types: - beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete, afterDelete - Workflows (*.workflow.yml): approval processes, automation -**3. Presentation Layer:** -- Pages (*.page.yml): composable UI pages with layouts -- Views (*.view.yml): list views, kanban, calendar displays -- Forms (*.form.yml): data entry forms with field layouts -- Reports (*.report.yml): tabular, summary, matrix reports -- Menus (*.menu.yml): navigation structure - -**4. Security Layer:** +**3. Security Layer:** - Permissions (*.permission.yml): access control rules - Application (*.application.yml): app-level configuration @@ -696,7 +680,6 @@ Include: - 2-3 core objects with essential fields - Basic relationships between objects - Simple validation rules -- At least one form and view per object - At least one action with TypeScript implementation - At least one hook with TypeScript implementation @@ -708,21 +691,15 @@ Output: Provide each file separately with clear filename headers (e.g., "# filen Include ALL necessary metadata types WITH implementations: 1. **Objects**: All entities with comprehensive fields 2. **Validations**: Business rules and constraints -3. **Forms**: Create and edit forms for each object -4. **Views**: List views for browsing data -5. **Pages**: Dashboard and detail pages -6. **Menus**: Navigation structure -7. **Actions WITH TypeScript implementations**: Common operations (approve, export, etc.) - Generate BOTH .yml metadata AND .action.ts implementation files -8. **Hooks WITH TypeScript implementations**: Lifecycle triggers - Generate .hook.ts implementation files -9. **Permissions**: Basic access control -10. **Data**: Sample seed data (optional) -11. **Workflows**: Approval processes if applicable -12. **Reports**: Key reports for analytics -13. **Tests**: Generate test files (.test.ts) for actions and hooks to validate business logic +3. **Actions WITH TypeScript implementations**: Common operations (approve, export, etc.) - Generate BOTH .yml metadata AND .action.ts implementation files +4. **Hooks WITH TypeScript implementations**: Lifecycle triggers - Generate .hook.ts implementation files +5. **Permissions**: Basic access control +6. **Data**: Sample seed data (optional) +7. **Workflows**: Approval processes if applicable +8. **Tests**: Generate test files (.test.ts) for actions and hooks to validate business logic Consider: - Security and permissions from the start -- User experience in form/view design - Business processes and workflows - Data integrity and validation - Complete TypeScript implementations for all actions and hooks @@ -863,15 +840,10 @@ Provide feedback in the specified format.`; private inferFileType(filename: string): GenerateAppResult['files'][0]['type'] { if (filename.includes('.object.yml')) return 'object'; if (filename.includes('.validation.yml')) return 'validation'; - if (filename.includes('.form.yml')) return 'form'; - if (filename.includes('.view.yml')) return 'view'; - if (filename.includes('.page.yml')) return 'page'; - if (filename.includes('.menu.yml')) return 'menu'; if (filename.includes('.action.yml')) return 'action'; if (filename.includes('.hook.yml')) return 'hook'; if (filename.includes('.permission.yml')) return 'permission'; if (filename.includes('.workflow.yml')) return 'workflow'; - if (filename.includes('.report.yml')) return 'report'; if (filename.includes('.data.yml')) return 'data'; if (filename.includes('.application.yml') || filename.includes('.app.yml')) return 'application'; if (filename.includes('.action.ts') || filename.includes('.hook.ts')) return 'typescript'; diff --git a/packages/foundation/platform-node/test/fixtures/test_dashboard.page.yml b/packages/foundation/platform-node/test/fixtures/test_dashboard.page.yml deleted file mode 100644 index 5d3ec901..00000000 --- a/packages/foundation/platform-node/test/fixtures/test_dashboard.page.yml +++ /dev/null @@ -1,42 +0,0 @@ -# Test Dashboard Page -name: test_dashboard -label: Test Dashboard -description: Dashboard layout test -icon: dashboard -layout: dashboard - -components: - # Metric with grid positioning - - id: metric_1 - type: metric - label: Total Count - data_source: - object: projects - query: - op: count - config: - format: number - color: blue - grid: - x: 0 - y: 0 - w: 3 - h: 2 - - # Chart with grid positioning - - id: chart_1 - type: chart - label: Projects by Status - data_source: - object: projects - fields: ['status'] - config: - chart_type: bar - grid: - x: 3 - y: 0 - w: 6 - h: 4 - -permissions: - view: ['admin', 'manager'] diff --git a/packages/foundation/platform-node/test/fixtures/test_page.page.yml b/packages/foundation/platform-node/test/fixtures/test_page.page.yml deleted file mode 100644 index c38b4b27..00000000 --- a/packages/foundation/platform-node/test/fixtures/test_page.page.yml +++ /dev/null @@ -1,54 +0,0 @@ -# Test Page - Simple Single Column Layout -name: test_page -label: Test Page -description: A test page for unit tests -icon: test-tube -layout: single_column - -# Permissions -permissions: - view: ['admin', 'user'] - edit: ['admin'] - -# Simple components -components: - - id: header_text - type: text - label: Welcome - config: - content: Welcome to the test page - format: text - - - id: test_grid - type: data_grid - label: Test Data - data_source: - object: projects - fields: ['name', 'status'] - sort: [['created_at', 'desc']] - limit: 10 - config: - columns: - - field: name - label: Project Name - - field: status - label: Status - actions: - on_click: - type: navigate - path: /projects/{{id}} - - - id: action_button - type: button - label: Click Me - actions: - on_click: - type: run_action - object: projects - action: custom_action - success_message: Action completed - -# AI context -ai_context: - intent: Test page for unit testing - persona: Test users diff --git a/packages/foundation/platform-node/test/fixtures/test_responsive.page.yml b/packages/foundation/platform-node/test/fixtures/test_responsive.page.yml deleted file mode 100644 index 3558df6d..00000000 --- a/packages/foundation/platform-node/test/fixtures/test_responsive.page.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Test Responsive Page -name: test_responsive -label: Test Responsive -description: Page with responsive configuration -icon: mobile -layout: single_column - -# Responsive breakpoints -responsive: - mobile: - columns: 1 - visible: true - tablet: - columns: 2 - visible: true - desktop: - columns: 3 - visible: true - -components: - - id: responsive_grid - type: data_grid - label: Responsive Grid - data_source: - object: projects - fields: ['name'] - responsive: - mobile: - visible: true - columns: 1 - tablet: - visible: true - columns: 2 - desktop: - visible: true - columns: 3 - -permissions: - view: ['*'] diff --git a/packages/foundation/platform-node/test/fixtures/test_sections.page.yml b/packages/foundation/platform-node/test/fixtures/test_sections.page.yml deleted file mode 100644 index 815c774a..00000000 --- a/packages/foundation/platform-node/test/fixtures/test_sections.page.yml +++ /dev/null @@ -1,45 +0,0 @@ -# Test Sections Page -name: test_sections -label: Test Sections -description: Two-column layout with sections -icon: layout -layout: two_column - -sections: - # Main content section - - id: main_section - type: content - label: Main Content - style: - width: 70% - components: - - id: content_form - type: form - label: Edit Form - data_source: - object: projects - config: - mode: edit - fields: - - name: name - label: Name - type: text - - name: description - label: Description - type: textarea - - # Sidebar section - - id: sidebar_section - type: sidebar - label: Sidebar - style: - width: 30% - components: - - id: stats - type: metric - label: Statistics - config: - format: number - -permissions: - view: ['admin'] diff --git a/packages/foundation/platform-node/test/page.test.ts b/packages/foundation/platform-node/test/page.test.ts deleted file mode 100644 index 392cc41b..00000000 --- a/packages/foundation/platform-node/test/page.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ObjectLoader } from '../src/loader'; -import { MetadataRegistry } from '@objectql/types'; -import * as path from 'path'; -import * as fs from 'fs'; - -describe('Page Metadata Loader', () => { - let registry: MetadataRegistry; - let loader: ObjectLoader; - - beforeEach(() => { - registry = new MetadataRegistry(); - loader = new ObjectLoader(registry); - }); - - it('should load page from .page.yml file', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const pages = registry.list('page'); - expect(pages.length).toBeGreaterThan(0); - }); - - it('should parse page metadata correctly', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_page'); - expect(page).toBeDefined(); - expect(page.label).toBe('Test Page'); - expect(page.layout).toBe('single_column'); - expect(page.components).toBeDefined(); - expect(Array.isArray(page.components)).toBe(true); - }); - - it('should load page with dashboard layout', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_dashboard'); - expect(page).toBeDefined(); - expect(page.layout).toBe('dashboard'); - expect(page.components).toBeDefined(); - - // Check if components have grid positions - const componentWithGrid = page.components?.find((c: any) => c.grid); - expect(componentWithGrid).toBeDefined(); - if (componentWithGrid && componentWithGrid.grid) { - expect(componentWithGrid.grid.x).toBeDefined(); - expect(componentWithGrid.grid.y).toBeDefined(); - expect(componentWithGrid.grid.w).toBeDefined(); - expect(componentWithGrid.grid.h).toBeDefined(); - } - }); - - it('should load page with sections', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_sections'); - expect(page).toBeDefined(); - expect(page.sections).toBeDefined(); - expect(Array.isArray(page.sections)).toBe(true); - - if (page.sections && page.sections.length > 0) { - const section = page.sections[0]; - expect(section.id).toBeDefined(); - expect(section.components).toBeDefined(); - } - }); - - it('should support page permissions', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_page'); - expect(page).toBeDefined(); - expect(page.permissions).toBeDefined(); - expect(page.permissions.view).toBeDefined(); - expect(Array.isArray(page.permissions.view)).toBe(true); - }); - - it('should support component actions', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_page'); - expect(page).toBeDefined(); - - const componentWithAction = page.components?.find((c: any) => c.actions); - expect(componentWithAction).toBeDefined(); - if (componentWithAction && componentWithAction.actions) { - expect(componentWithAction.actions.on_click).toBeDefined(); - } - }); - - it('should support data source configuration', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_page'); - expect(page).toBeDefined(); - - const componentWithDataSource = page.components?.find((c: any) => c.data_source); - expect(componentWithDataSource).toBeDefined(); - if (componentWithDataSource && componentWithDataSource.data_source) { - expect(componentWithDataSource.data_source.object).toBeDefined(); - } - }); - - it('should support responsive configuration', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_responsive'); - expect(page).toBeDefined(); - expect(page.responsive).toBeDefined(); - - if (page.responsive) { - expect(page.responsive.mobile).toBeDefined(); - expect(page.responsive.tablet).toBeDefined(); - expect(page.responsive.desktop).toBeDefined(); - } - }); - - it('should support AI context', () => { - const fixturesDir = path.join(__dirname, 'fixtures'); - loader.load(fixturesDir); - - const page = registry.get('page', 'test_page'); - expect(page).toBeDefined(); - expect(page.ai_context).toBeDefined(); - - if (page.ai_context) { - expect(page.ai_context.intent).toBeDefined(); - } - }); -}); diff --git a/packages/runtime/server/src/index.ts b/packages/runtime/server/src/index.ts index 2c4239b2..de49a20d 100644 --- a/packages/runtime/server/src/index.ts +++ b/packages/runtime/server/src/index.ts @@ -3,7 +3,6 @@ export * from './utils'; export * from './openapi'; export * from './server'; export * from './metadata'; -export * from './studio'; export * from './storage'; export * from './file-handler'; // We export createNodeHandler from root for convenience, diff --git a/packages/runtime/server/src/studio.ts b/packages/runtime/server/src/studio.ts deleted file mode 100644 index c81aa169..00000000 --- a/packages/runtime/server/src/studio.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { IncomingMessage, ServerResponse } from 'http'; -import * as fs from 'fs'; -import * as path from 'path'; - -/** - * Creates a handler to serve the Studio UI static files. - */ -export function createStudioHandler() { - let distPath: string | null = null; - - // 1. Try to resolve from installed package (Standard way) - try { - const studioPkg = require.resolve('@objectql/studio/package.json'); - const candidate = path.join(path.dirname(studioPkg), 'dist'); - if (fs.existsSync(candidate)) { - distPath = candidate; - } - } catch (e) { - // @objectql/studio might not be installed - } - - // 2. Fallback for local development (Monorepo) - if (!distPath) { - const possiblePaths = [ - path.join(__dirname, '../../studio/dist'), - path.join(process.cwd(), 'packages/studio/dist'), - path.join(process.cwd(), 'packages/tools/studio/dist'), - ]; - - for (const p of possiblePaths) { - if (fs.existsSync(p)) { - distPath = p; - break; - } - } - } - - return async (req: IncomingMessage, res: ServerResponse) => { - if (!distPath) { - // Return placeholder page if studio is not built - const html = getPlaceholderPage(); - res.setHeader('Content-Type', 'text/html'); - res.statusCode = 200; - res.end(html); - return; - } - - // Parse the URL and remove /studio prefix - let urlPath = (req.url || '').replace(/^\/studio/, '') || '/'; - - // Default to index.html for SPA routing - if (urlPath === '/' || !urlPath.includes('.')) { - urlPath = '/index.html'; - } - - const filePath = path.join(distPath, urlPath); - - // Security check: ensure the file is within distPath - if (!filePath.startsWith(distPath)) { - res.statusCode = 403; - res.end('Forbidden'); - return; - } - - // Check if file exists - if (!fs.existsSync(filePath)) { - // For SPA, return index.html for any non-existent routes - const indexPath = path.join(distPath, 'index.html'); - if (fs.existsSync(indexPath)) { - const content = fs.readFileSync(indexPath); - res.setHeader('Content-Type', 'text/html'); - res.statusCode = 200; - res.end(content); - return; - } - - res.statusCode = 404; - res.end('Not Found'); - return; - } - - // Read and serve the file - const content = fs.readFileSync(filePath); - const ext = path.extname(filePath); - const contentType = getContentType(ext); - - res.setHeader('Content-Type', contentType); - res.statusCode = 200; - res.end(content); - }; -} - -function getContentType(ext: string): string { - const types: Record = { - '.html': 'text/html', - '.js': 'application/javascript', - '.css': 'text/css', - '.json': 'application/json', - '.png': 'image/png', - '.jpg': 'image/jpeg', - '.gif': 'image/gif', - '.svg': 'image/svg+xml', - '.ico': 'image/x-icon', - }; - return types[ext] || 'application/octet-stream'; -} - -function getPlaceholderPage(): string { - return ` - - - - - ObjectQL Studio - - - -
-

ObjectQL Studio

-

Web-based admin studio for database management

-
-

- The studio is available but needs to be built separately. -

-

- To use the full studio UI, run:
- cd packages/studio && pnpm run build -

-
-
- -`; -} diff --git a/packages/starters/basic/src/main.menu.yml b/packages/starters/basic/src/main.menu.yml deleted file mode 100644 index 6df5cb96..00000000 --- a/packages/starters/basic/src/main.menu.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: main -label: Main Menu -type: sidebar -app: demo_app - -items: - - name: core_business - label: Core Business - icon: briefcase - type: section - items: - - object: projects - - object: tasks - - - name: showcase - label: Showcase - icon: flask - type: section - items: - - object: kitchen_sink - label: Component Gallery - - - name: reporting - label: Reporting - icon: bar-chart - type: section - items: - - type: url - label: Dashboard - path: /dashboard diff --git a/packages/starters/basic/src/modules/projects/pages/create_project_wizard.page.yml b/packages/starters/basic/src/modules/projects/pages/create_project_wizard.page.yml deleted file mode 100644 index 01b1b988..00000000 --- a/packages/starters/basic/src/modules/projects/pages/create_project_wizard.page.yml +++ /dev/null @@ -1,244 +0,0 @@ -# Project Creation Wizard -# A multi-step wizard for creating new projects - -name: create_project_wizard -label: Create New Project -description: Step-by-step wizard for creating a project -icon: plus-circle -layout: wizard - -# Only admins and managers can create projects -permissions: - view: ['admin', 'manager'] - -# Wizard steps -components: - # Step 1: Basic Information - - id: step_basic - type: container - label: Basic Information - description: Enter the project name and description - config: - step: 1 - icon: info - components: - - id: basic_form - type: form - config: - layout: vertical - fields: - - name: name - label: Project Name - type: text - required: true - placeholder: My Awesome Project - help_text: Choose a descriptive name for your project - - name: description - label: Project Description - type: textarea - rows: 5 - placeholder: Describe what this project is about - - name: category - label: Category - type: select - options: - - label: Development - value: development - - label: Marketing - value: marketing - - label: Research - value: research - - label: Operations - value: operations - actions: - on_validate: - type: custom - handler: validateBasicInfo - - # Step 2: Team & Resources - - id: step_team - type: container - label: Team & Resources - description: Assign team members and set budget - config: - step: 2 - icon: users - components: - - id: team_form - type: form - config: - layout: vertical - fields: - - name: owner - label: Project Manager - type: lookup - reference_to: users - required: true - help_text: Select the person responsible for this project - - name: team_members - label: Team Members - type: lookup - reference_to: users - multiple: true - help_text: Add team members who will work on this project - - name: budget - label: Budget - type: currency - currency: USD - help_text: Estimated budget for this project - - name: department - label: Department - type: select - options: - - label: Engineering - value: engineering - - label: Product - value: product - - label: Sales - value: sales - - label: Marketing - value: marketing - - # Step 3: Timeline - - id: step_timeline - type: container - label: Timeline - description: Set project dates and milestones - config: - step: 3 - icon: calendar - components: - - id: timeline_form - type: form - config: - layout: vertical - fields: - - name: start_date - label: Start Date - type: date - required: true - defaultValue: '{{today}}' - - name: end_date - label: Target End Date - type: date - required: true - help_text: Expected completion date - - name: priority - label: Priority - type: select - required: true - options: - - label: Low - value: low - - label: Medium - value: medium - - label: High - value: high - - label: Critical - value: critical - defaultValue: medium - - name: milestones - label: Key Milestones - type: grid - help_text: Define important project milestones - config: - columns: - - name: name - label: Milestone - type: text - - name: date - label: Target Date - type: date - - name: description - label: Description - type: text - allow_add: true - allow_delete: true - - # Step 4: Review & Create - - id: step_review - type: container - label: Review & Create - description: Review your project details before creating - config: - step: 4 - icon: check-square - components: - - id: review_summary - type: detail_view - label: Project Summary - config: - mode: readonly - sections: - - label: Basic Information - fields: ['name', 'description', 'category'] - - label: Team - fields: ['owner', 'team_members', 'budget', 'department'] - - label: Timeline - fields: ['start_date', 'end_date', 'priority'] - - - id: terms_checkbox - type: container - components: - - id: accept_terms - type: boolean - label: I confirm that all information is correct - required: true - -# Wizard navigation actions -actions: - next_step: - type: custom - handler: validateAndProceed - - previous_step: - type: custom - handler: goToPreviousStep - - submit_wizard: - type: run_action - object: projects - action: create - success_message: Project created successfully! - on_error: show_modal - - save_draft: - type: custom - handler: saveDraft - success_message: Draft saved - -# Page state to track wizard progress -state: - initial: - current_step: 1 - completed_steps: [] - form_data: - name: '' - description: '' - category: '' - owner: null - team_members: [] - budget: 0 - department: '' - start_date: null - end_date: null - priority: medium - milestones: [] - persist: true - storage_key: project_wizard_draft - -# Page styling -style: - max_width: 800px - margin: 0 auto - padding: 40px 20px - -# AI context -ai_context: - intent: Guide users through project creation with validation - persona: Project managers and team leads - tasks: - - Create new projects step by step - - Validate input at each stage - - Review before submission - - Save drafts for later diff --git a/packages/starters/basic/src/modules/projects/pages/project_detail.page.yml b/packages/starters/basic/src/modules/projects/pages/project_detail.page.yml deleted file mode 100644 index 23fb9a28..00000000 --- a/packages/starters/basic/src/modules/projects/pages/project_detail.page.yml +++ /dev/null @@ -1,258 +0,0 @@ -# Project Detail Page -# A page for viewing and editing project details with a clean layout - -name: project_detail -label: Project Details -description: View and edit project information -icon: folder -layout: two_column - -# Page permissions -permissions: - view: ['admin', 'manager', 'user'] - edit: ['admin', 'manager'] - -# Sections-based layout for two-column design -sections: - # Left Column - Main Content - - id: main_content - type: content - style: - width: 70% - components: - # Project Information Form - - id: project_form - type: form - label: Project Information - data_source: - object: projects - query: - op: findOne - filter: [['_id', '=', '{{route.params.id}}']] - config: - mode: edit # view, edit, create - layout: vertical - fields: - - name: name - label: Project Name - type: text - required: true - placeholder: Enter project name - - name: description - label: Description - type: textarea - rows: 4 - - name: status - label: Status - type: select - options: - - label: Planning - value: planning - - label: Active - value: active - - label: On Hold - value: on_hold - - label: Completed - value: completed - - name: owner - label: Project Manager - type: lookup - reference_to: users - display_field: name - - name: start_date - label: Start Date - type: date - - name: end_date - label: End Date - type: date - - name: budget - label: Budget - type: currency - currency: USD - field_layout: - - row: [name] - - row: [description] - - row: [status, owner] - - row: [start_date, end_date] - - row: [budget] - actions: - on_submit: - type: run_action - object: projects - action: update - success_message: Project updated successfully - on_cancel: - type: navigate - path: /projects - style: - margin: 0 0 24px 0 - - # Tasks List - - id: project_tasks - type: data_grid - label: Tasks - data_source: - object: tasks - filters: - - ['project', '=', '{{route.params.id}}'] - fields: ['name', 'status', 'priority', 'assigned_to', 'due_date'] - sort: [['created_at', 'desc']] - expand: - assigned_to: - fields: ['name'] - config: - columns: - - field: name - label: Task - width: 250 - - field: status - label: Status - width: 120 - - field: priority - label: Priority - width: 100 - - field: assigned_to.name - label: Assigned To - width: 150 - - field: due_date - label: Due Date - width: 120 - toolbar_actions: - - label: New Task - icon: plus - action: create_task - row_actions: - - label: Edit - icon: edit - action: edit_task - - label: Delete - icon: trash - action: delete_task - confirm: Are you sure you want to delete this task? - actions: - on_load: - type: refresh - - # Right Column - Sidebar - - id: sidebar - type: sidebar - style: - width: 30% - padding: 0 0 0 24px - components: - # Project Metrics - - id: project_stats - type: container - label: Project Statistics - components: - - id: task_count - type: metric - label: Total Tasks - data_source: - object: tasks - filters: - - ['project', '=', '{{route.params.id}}'] - query: - op: count - config: - format: number - size: small - - - id: completed_tasks - type: metric - label: Completed - data_source: - object: tasks - filters: - - ['project', '=', '{{route.params.id}}'] - - 'and' - - ['status', '=', 'completed'] - query: - op: count - config: - format: number - size: small - color: green - - - id: progress - type: metric - label: Progress - config: - format: percent - value: '{{completed_tasks / task_count}}' - size: small - - # Activity Timeline - - id: activity_timeline - type: timeline - label: Recent Activity - data_source: - object: activities - filters: - - ['related_to', '=', '{{route.params.id}}'] - - 'and' - - ['entity_type', '=', 'project'] - fields: ['action', 'user', 'created_at', 'description'] - sort: [['created_at', 'desc']] - limit: 10 - config: - group_by: date - show_user_avatar: true - - # Quick Actions - - id: quick_actions - type: container - label: Quick Actions - components: - - id: archive_btn - type: button - label: Archive Project - config: - variant: outline - icon: archive - actions: - on_click: - type: run_action - object: projects - action: archive - confirm: Archive this project? - success_message: Project archived - - - id: export_btn - type: button - label: Export Data - config: - variant: outline - icon: download - actions: - on_click: - type: custom - handler: exportProjectData - -# Responsive configuration -responsive: - mobile: - # Stack columns vertically on mobile - columns: 1 - tablet: - columns: 1 - desktop: - columns: 2 - -# Page-level state -state: - initial: - edit_mode: false - persist: true - storage_key: project_detail_state - -# AI context -ai_context: - intent: View and manage individual project details with tasks and metrics - persona: Project managers and team members - tasks: - - View project information - - Update project details - - Manage tasks within the project - - Track project progress - - View activity history diff --git a/packages/starters/basic/src/modules/projects/project_status.report.yml b/packages/starters/basic/src/modules/projects/project_status.report.yml deleted file mode 100644 index 710056a6..00000000 --- a/packages/starters/basic/src/modules/projects/project_status.report.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: project_status_summary -label: "Project Status Report" -type: summary -object: projects -description: "Overview of projects grouped by status and priority" - -# Columns to display in details -columns: - - field: name - label: Project Name - - field: owner - label: Owner - - field: budget - label: Budget - format: currency - -# Grouping Configuration -groupings: - - field: status - label: Status - - - field: priority - label: Priority - -# Aggregations -aggregations: - - field: budget - function: sum - label: Total Budget - - - field: _id - function: count - label: Project Count - -# Default Filters -filters: - - field: created_at - operator: ">=" - value: "2024-01-01" diff --git a/packages/starters/basic/src/modules/projects/projects.form.yml b/packages/starters/basic/src/modules/projects/projects.form.yml deleted file mode 100644 index 0aa02062..00000000 --- a/packages/starters/basic/src/modules/projects/projects.form.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: project_main_form -label: "Project Details" -type: edit -object: projects -description: "Main form for creating and editing projects" - -layout: - tabs: - - label: "General Info" - sections: - - label: "Basic Details" - columns: 2 - fields: - - name - - owner - - status - - priority - - - label: "Description" - columns: 1 - fields: - - description - - - label: "Planning" - sections: - - label: "Schedule & Budget" - columns: 2 - fields: - - start_date - - end_date - - budget - - - label: "Approval Info" - sections: - - label: "Audit Trail" - fields: - - name: approved_by - readonly: true - - name: approved_at - readonly: true - - approval_comment diff --git a/packages/starters/basic/src/modules/projects/projects.view.yml b/packages/starters/basic/src/modules/projects/projects.view.yml deleted file mode 100644 index 51cb7681..00000000 --- a/packages/starters/basic/src/modules/projects/projects.view.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: all_projects -label: "Active Projects" -type: list -object: projects -description: "Standard list view of all active projects" - -config: - columns: - - field: name - width: 250 - - field: status - width: 120 - - field: priority - width: 100 - - field: owner - width: 150 - - field: budget - width: 120 - - field: end_date - width: 120 - - sort: - - field: created_at - direction: desc - - filters: - - field: status - operator: "!=" - value: "archived" - - actions: - - edit - - delete - - clone - - custom: approve diff --git a/packages/starters/basic/src/pages/dashboard.page.yml b/packages/starters/basic/src/pages/dashboard.page.yml deleted file mode 100644 index 794d1074..00000000 --- a/packages/starters/basic/src/pages/dashboard.page.yml +++ /dev/null @@ -1,206 +0,0 @@ -# Dashboard Page Example -# A comprehensive dashboard showing project metrics and tasks - -name: dashboard -label: Project Dashboard -description: Overview of projects, tasks, and key metrics -icon: dashboard -layout: dashboard - -# Page-level permissions -permissions: - view: ['admin', 'manager', 'user'] - edit: ['admin', 'manager'] - -# SEO metadata -meta: - title: Project Dashboard - ObjectQL Demo - description: Real-time overview of project status and team performance - -# Real-time updates -realtime: true - -# Components arranged in a grid layout -components: - # KPI Metrics Row - - id: total_projects - type: metric - label: Total Projects - data_source: - object: projects - query: - op: count - config: - format: number - icon: folder - color: blue - grid: - x: 0 - y: 0 - w: 3 - h: 2 - - - id: active_tasks - type: metric - label: Active Tasks - data_source: - object: tasks - filters: - - ['status', 'in', ['in_progress', 'pending']] - query: - op: count - config: - format: number - icon: check-circle - color: green - grid: - x: 3 - y: 0 - w: 3 - h: 2 - - - id: overdue_tasks - type: metric - label: Overdue Tasks - data_source: - object: tasks - filters: - - ['due_date', '<', '{{today}}'] - - 'and' - - ['status', '!=', 'completed'] - query: - op: count - config: - format: number - icon: alert-circle - color: red - grid: - x: 6 - y: 0 - w: 3 - h: 2 - - - id: completion_rate - type: metric - label: Completion Rate - data_source: - object: tasks - query: - op: aggregate - function: avg - field: completed - config: - format: percent - icon: trending-up - color: purple - grid: - x: 9 - y: 0 - w: 3 - h: 2 - - # Charts Row - - id: projects_by_status - type: chart - label: Projects by Status - data_source: - object: projects - fields: ['status'] - query: - op: group_by - field: status - aggregate: count - config: - chart_type: pie - colors: ['#10b981', '#3b82f6', '#f59e0b', '#ef4444'] - grid: - x: 0 - y: 2 - w: 6 - h: 4 - - - id: tasks_timeline - type: chart - label: Tasks Completion Timeline - data_source: - object: tasks - fields: ['created_at', 'completed_at'] - query: - op: time_series - field: created_at - interval: week - config: - chart_type: line - show_trend: true - grid: - x: 6 - y: 2 - w: 6 - h: 4 - - # Recent Tasks List - - id: recent_tasks - type: data_grid - label: Recent Tasks - data_source: - object: tasks - fields: ['name', 'status', 'priority', 'due_date', 'assigned_to'] - sort: [['created_at', 'desc']] - limit: 10 - expand: - assigned_to: - fields: ['name', 'email'] - config: - columns: - - field: name - label: Task Name - width: 300 - - field: status - label: Status - width: 120 - badge: true - - field: priority - label: Priority - width: 100 - - field: due_date - label: Due Date - width: 120 - format: date - - field: assigned_to.name - label: Assigned To - width: 150 - row_actions: - - label: View - action: navigate - path: /tasks/{{id}} - - label: Edit - action: open_modal - modal: edit_task - enable_search: true - enable_filters: true - grid: - x: 0 - y: 6 - w: 12 - h: 6 - -# Page-level actions -actions: - refresh_data: - type: refresh - success_message: Data refreshed successfully - - export_dashboard: - type: custom - handler: exportDashboardData - confirm: Export dashboard data to CSV? - -# AI context for understanding this page -ai_context: - intent: Provide a comprehensive overview of project and task status - persona: Project managers and team leads - tasks: - - Monitor project progress - - Identify overdue tasks - - Track team performance - - Export data for reporting diff --git a/packages/starters/basic/src/pages/landing.page.yml b/packages/starters/basic/src/pages/landing.page.yml deleted file mode 100644 index c6047cb0..00000000 --- a/packages/starters/basic/src/pages/landing.page.yml +++ /dev/null @@ -1,275 +0,0 @@ -# Custom Landing Page -# A canvas layout for flexible positioning - -name: landing_page -label: Welcome -description: Custom landing page with free-form layout -icon: home -layout: canvas - -# Public page - no authentication required -permissions: - view: ['*'] - -# Components with absolute positioning -components: - # Hero Section - - id: hero_section - type: container - style: - position: absolute - top: 0 - left: 0 - width: 100% - height: 400px - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) - padding: 60px 20px - components: - - id: hero_title - type: text - config: - content: Welcome to ObjectQL - format: markdown - style: - font_size: 48px - font_weight: bold - color: white - text_align: center - margin: 0 0 20px 0 - - - id: hero_subtitle - type: text - config: - content: One Protocol, Any Database, AI-Ready - format: text - style: - font_size: 24px - color: white - text_align: center - opacity: 0.9 - - - id: cta_button - type: button - label: Get Started - config: - variant: primary - size: large - style: - margin: 40px auto 0 - display: block - width: 200px - actions: - on_click: - type: navigate - path: /dashboard - - # Features Grid - - id: features_section - type: container - style: - position: absolute - top: 450px - left: 50% - transform: translateX(-50%) - width: 90% - max_width: 1200px - components: - - id: features_title - type: text - config: - content: '## Key Features' - format: markdown - style: - text_align: center - margin: 0 0 40px 0 - - - id: features_grid - type: container - style: - display: grid - grid_template_columns: repeat(3, 1fr) - gap: 30px - components: - - id: feature_1 - type: container - style: - padding: 30px - background: white - border_radius: 8px - box_shadow: 0 2px 8px rgba(0,0,0,0.1) - components: - - id: feature_1_icon - type: image - config: - src: /icons/database.svg - alt: Database - style: - width: 64px - height: 64px - margin: 0 auto 20px - - id: feature_1_title - type: text - config: - content: '**Universal Engine**' - format: markdown - style: - text_align: center - margin: 0 0 10px 0 - - id: feature_1_desc - type: text - config: - content: Run on MongoDB or PostgreSQL with the same API - format: text - style: - text_align: center - color: '#666' - - - id: feature_2 - type: container - style: - padding: 30px - background: white - border_radius: 8px - box_shadow: 0 2px 8px rgba(0,0,0,0.1) - components: - - id: feature_2_icon - type: image - config: - src: /icons/ai.svg - alt: AI - style: - width: 64px - height: 64px - margin: 0 auto 20px - - id: feature_2_title - type: text - config: - content: '**AI-Native**' - format: markdown - style: - text_align: center - margin: 0 0 10px 0 - - id: feature_2_desc - type: text - config: - content: JSON-based queries perfect for LLM integration - format: text - style: - text_align: center - color: '#666' - - - id: feature_3 - type: container - style: - padding: 30px - background: white - border_radius: 8px - box_shadow: 0 2px 8px rgba(0,0,0,0.1) - components: - - id: feature_3_icon - type: image - config: - src: /icons/metadata.svg - alt: Metadata - style: - width: 64px - height: 64px - margin: 0 auto 20px - - id: feature_3_title - type: text - config: - content: '**Metadata-Driven**' - format: markdown - style: - text_align: center - margin: 0 0 10px 0 - - id: feature_3_desc - type: text - config: - content: Define schemas in YAML with built-in validation - format: text - style: - text_align: center - color: '#666' - - # Stats Section - - id: stats_section - type: container - style: - position: absolute - top: 900px - left: 0 - width: 100% - background: '#f7fafc' - padding: 60px 20px - components: - - id: stats_grid - type: container - style: - display: grid - grid_template_columns: repeat(4, 1fr) - gap: 40px - max_width: 1200px - margin: 0 auto - components: - - id: stat_1 - type: metric - label: Objects Created - data_source: - object: objects - query: - op: count - config: - format: number - size: large - color: blue - - - id: stat_2 - type: metric - label: Total Records - data_source: - object: records - query: - op: count - config: - format: number - size: large - color: green - - - id: stat_3 - type: metric - label: API Calls Today - config: - value: 12453 - format: number - size: large - color: purple - - - id: stat_4 - type: metric - label: Uptime - config: - value: 99.9 - format: percent - size: large - color: orange - -# Responsive overrides -responsive: - mobile: - # Override grid layouts to single column - visible: true - tablet: - visible: true - -# No real-time updates needed for landing page -realtime: false - -# AI context -ai_context: - intent: Introduce users to ObjectQL and showcase key features - persona: New visitors and potential users - tasks: - - Learn about ObjectQL - - Understand key features - - Navigate to dashboard diff --git a/packages/starters/enterprise/src/menus/erp.menu.yml b/packages/starters/enterprise/src/menus/erp.menu.yml deleted file mode 100644 index 3c1a59cc..00000000 --- a/packages/starters/enterprise/src/menus/erp.menu.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: erp_main -label: ERP Main Menu -app: erp -type: sidebar -items: - - name: projects - label: Projects - type: section - items: - - object: project_project - - object: project_task diff --git a/packages/starters/express-api/__tests__/metadata-loading.test.ts b/packages/starters/express-api/__tests__/metadata-loading.test.ts index 58f5d470..7887b8e5 100644 --- a/packages/starters/express-api/__tests__/metadata-loading.test.ts +++ b/packages/starters/express-api/__tests__/metadata-loading.test.ts @@ -133,15 +133,4 @@ describe('Metadata Loading', () => { expect(userMetadata.name).toBe('user'); }); }); - - describe('View Metadata', () => { - it('should load view metadata if present', () => { - const views = app.metadata.list('view'); - - // The express-api starter has test.view.yml - if (views && views.length > 0) { - expect(views.length).toBeGreaterThanOrEqual(1); - } - }); - }); }); diff --git a/packages/starters/express-api/src/index.ts b/packages/starters/express-api/src/index.ts index fe2cdf9b..e8876692 100644 --- a/packages/starters/express-api/src/index.ts +++ b/packages/starters/express-api/src/index.ts @@ -2,7 +2,7 @@ import express from 'express'; import { ObjectQL } from '@objectql/core'; import { SqlDriver } from '@objectql/driver-sql'; import { ObjectLoader } from '@objectql/platform-node'; -import { createNodeHandler, createMetadataHandler, createStudioHandler, createRESTHandler } from '@objectql/server'; +import { createNodeHandler, createMetadataHandler, createRESTHandler } from '@objectql/server'; import * as path from 'path'; async function main() { @@ -29,7 +29,6 @@ app.init().then(async () => { const objectQLHandler = createNodeHandler(app); const restHandler = createRESTHandler(app); const metadataHandler = createMetadataHandler(app); - const studioHandler = createStudioHandler(); // 4. Setup Express const server = express(); @@ -51,7 +50,6 @@ app.init().then(async () => { server.all('/api/objectql*', objectQLHandler); server.all('/api/data/*', restHandler); server.all('/api/metadata*', metadataHandler); - server.get('/studio*', studioHandler); // Create some sample data const ctx = app.createContext({ isSystem: true }); @@ -66,7 +64,6 @@ app.init().then(async () => { server.listen(port, () => { console.log(`\n🚀 ObjectQL Server running on http://localhost:${port}`); - console.log(`\n📊 Console UI: http://localhost:${port}/console`); console.log(`\n🔌 APIs:`); console.log(` - JSON-RPC: http://localhost:${port}/api/objectql`); console.log(` - REST: http://localhost:${port}/api/data`); diff --git a/packages/starters/express-api/src/test.view.yml b/packages/starters/express-api/src/test.view.yml deleted file mode 100644 index 24798177..00000000 --- a/packages/starters/express-api/src/test.view.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: test-view -label: Test View -description: A sample view for testing metadata loading -query: select * from users -fields: - - name: id - label: ID - - name: name - label: Name diff --git a/packages/tools/cli/package.json b/packages/tools/cli/package.json index 5a2e67d7..9848b7ca 100644 --- a/packages/tools/cli/package.json +++ b/packages/tools/cli/package.json @@ -22,7 +22,6 @@ "scripts": { "build": "tsc", "watch": "tsc -w", - "studio": "node dist/index.js studio", "test": "jest" }, "dependencies": { diff --git a/packages/tools/cli/src/commands/ai.ts b/packages/tools/cli/src/commands/ai.ts index 71afb374..85f4d096 100644 --- a/packages/tools/cli/src/commands/ai.ts +++ b/packages/tools/cli/src/commands/ai.ts @@ -263,10 +263,11 @@ export async function aiValidate(options: ValidateOptions): Promise { const patterns = [ '**/*.object.yml', '**/*.validation.yml', - '**/*.form.yml', - '**/*.view.yml', - '**/*.page.yml', '**/*.action.yml', + '**/*.hook.yml', + '**/*.permission.yml', + '**/*.workflow.yml', + '**/*.data.yml', ]; const files = await glob(patterns, { diff --git a/packages/tools/cli/src/commands/new.ts b/packages/tools/cli/src/commands/new.ts index 56822466..49b7ecac 100644 --- a/packages/tools/cli/src/commands/new.ts +++ b/packages/tools/cli/src/commands/new.ts @@ -11,16 +11,11 @@ interface NewOptions { const METADATA_TYPES = [ 'object', - 'view', - 'form', - 'page', 'action', 'hook', 'permission', 'validation', 'workflow', - 'report', - 'menu', 'data' ]; @@ -35,35 +30,6 @@ const TEMPLATES: Record = { } } }, - view: { - label: '{{label}} View', - object: '{{objectName}}', - columns: [ - { field: 'name', label: 'Name' } - ] - }, - form: { - label: '{{label}} Form', - object: '{{objectName}}', - layout: { - sections: [ - { - label: 'Basic Information', - fields: ['name'] - } - ] - } - }, - page: { - label: '{{label}} Page', - type: 'standard', - components: [ - { - type: 'title', - text: '{{label}}' - } - ] - }, action: { label: '{{label}} Action', object: '{{objectName}}', @@ -118,24 +84,6 @@ const TEMPLATES: Record = { } ] }, - report: { - label: '{{label}} Report', - type: 'tabular', - object: '{{objectName}}', - columns: [ - { field: 'name', label: 'Name' } - ] - }, - menu: { - label: '{{label}} Menu', - items: [ - { - label: 'Home', - page: 'home', - icon: 'home' - } - ] - }, data: { label: '{{label}} Data', object: '{{objectName}}', diff --git a/packages/tools/cli/src/commands/studio.ts b/packages/tools/cli/src/commands/studio.ts deleted file mode 100644 index 71adae89..00000000 --- a/packages/tools/cli/src/commands/studio.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { ObjectQL } from '@objectql/core'; -import { ObjectLoader, createDriverFromConnection } from '@objectql/platform-node'; -import { createNodeHandler, createStudioHandler, createMetadataHandler } from '@objectql/server'; -import { createServer } from 'http'; -import * as path from 'path'; -import * as fs from 'fs'; -import chalk from 'chalk'; -import { exec } from 'child_process'; -import { register } from 'ts-node'; -import glob from 'fast-glob'; - -const SWAGGER_HTML = ` - - - - - - ObjectQL Swagger UI - - - - -
- - - - -`; - -function openBrowser(url: string) { - const start = (process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open'); - exec(`${start} ${url}`); -} - -export async function startStudio(options: { port: number; dir: string, open?: boolean }) { - const startPort = options.port || 5555; - const rootDir = path.resolve(process.cwd(), options.dir || '.'); - - console.log(chalk.blue('Starting ObjectQL Studio...')); - console.log(chalk.gray(`Project Root: ${rootDir}`)); - - // Register ts-node - register({ - transpileOnly: true, - compilerOptions: { - module: "commonjs" - } - }); - - let app: ObjectQL; - const configTs = path.join(rootDir, 'objectql.config.ts'); - const configJs = path.join(rootDir, 'objectql.config.js'); - - if (fs.existsSync(configTs)) { - console.log(chalk.gray(`Loading config from ${configTs}`)); - const mod = require(configTs); - app = mod.default || mod; - } else if (fs.existsSync(configJs)) { - console.log(chalk.gray(`Loading config from ${configJs}`)); - const mod = require(configJs); - app = mod.default || mod; - } else { - console.error(chalk.red('\n❌ Error: Configuration file (objectql.config.ts) not found.')); - process.exit(1); - } - - if (!app) { - console.error(chalk.red('\n❌ Error: No default export found in configuration file.')); - process.exit(1); - } - - // Initialize App if it's a configuration object - if (typeof (app as any).init !== 'function') { - const config = app as any; - console.log(chalk.gray('Configuration object detected. Initializing ObjectQL instance...')); - - const datasources: any = {}; - - if (config.datasource && config.datasource.default) { - const dbConfig = config.datasource.default; - if (dbConfig.type === 'sqlite') { - try { - const { SqlDriver } = require('@objectql/driver-sql'); - datasources.default = new SqlDriver({ - client: 'sqlite3', - connection: { - filename: dbConfig.filename ? path.resolve(rootDir, dbConfig.filename) : ':memory:' - }, - useNullAsDefault: true - }); - } catch (e) { - console.warn(chalk.yellow('Failed to load @objectql/driver-sql. Ensure it is installed.')); - } - } - } - - // Fallback to memory if no datasource - if (!datasources.default) { - console.warn(chalk.yellow('No valid datasource found. Using in-memory SQLite.')); - const { SqlDriver } = require('@objectql/driver-sql'); - datasources.default = new SqlDriver({ - client: 'sqlite3', - connection: { filename: ':memory:' }, - useNullAsDefault: true - }); - } - - app = new ObjectQL({ datasources }); - - // Load Schema - const loader = new ObjectLoader(app.metadata); - - // Load Presets - if (Array.isArray(config.presets)) { - console.log(chalk.gray(`Loading ${config.presets.length} presets...`)); - for (const preset of config.presets) { - try { - loader.loadPackage(preset); - } catch (e: any) { - console.warn(chalk.yellow(`Failed to load preset ${preset}:`), e.message); - } - } - } - - // Load Schema from Directory - // In a monorepo root with presets, scanning everything is dangerous. - // We check if we are in a monorepo-like environment. - const isMonorepoRoot = fs.existsSync(path.join(rootDir, 'pnpm-workspace.yaml')) || fs.existsSync(path.join(rootDir, 'pnpm-lock.yaml')); - - // If we are in a likely monorepo root AND have presets, skip recursive scan - if (isMonorepoRoot && config.presets && config.presets.length > 0) { - console.log(chalk.yellow('Monorepo root detected with presets. Skipping recursive file scan of root directory to avoid conflicts.')); - } else { - loader.load(rootDir); - } - } - - // 2. Load Schema & Init - try { - await app.init(); - } catch (e: any) { - console.error(chalk.red('❌ Failed to initialize application:'), e.message); - process.exit(1); - } - - // 3. Setup HTTP Server - const nodeHandler = createNodeHandler(app); - const studioHandler = createStudioHandler(); - const metadataHandler = createMetadataHandler(app); - - const server = createServer(async (req, res) => { - // CORS - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - - if (req.method === 'OPTIONS') { - res.writeHead(200); - res.end(); - return; - } - - if (req.url === '/openapi.json') { - return nodeHandler(req, res); - } - - if (req.url === '/swagger') { - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end(SWAGGER_HTML); - return; - } - - // Routing - if (req.url?.startsWith('/studio')) { - return studioHandler(req, res); - } - - if (req.url?.startsWith('/api/schema/files')) { - // List all metadata files - try { - // Find all *.*.yml files relative to rootDir - // Note: User might have configured objectql with specific source paths. - // We ignore common build folders to avoid duplicates/editing compiled files. - // We broadly match all objectql-like files: *.object.yml, *.view.yml, etc. - const files = await glob('**/*.{object,app,view,permission,report,validation,workflow,form,data,hook,action}.yml', { - cwd: rootDir, - ignore: ['node_modules/**', 'dist/**', 'build/**', 'out/**', '.git/**', '.next/**'] - }); - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ files })); - } catch (e: any) { - res.statusCode = 500; - res.end(JSON.stringify({ error: e.message })); - } - return; - } - - if (req.url?.startsWith('/api/schema/content')) { - const urlObj = new URL(req.url, `http://${req.headers.host}`); - const file = urlObj.searchParams.get('file'); - - if (!file) { - res.statusCode = 400; - res.end(JSON.stringify({ error: 'Missing file parameter' })); - return; - } - - const filePath = path.join(rootDir, file); - // Security check - if (!filePath.startsWith(rootDir)) { - res.statusCode = 403; - res.end(JSON.stringify({ error: 'Access denied' })); - return; - } - - if (req.method === 'GET') { - try { - const content = fs.readFileSync(filePath, 'utf-8'); - res.setHeader('Content-Type', 'text/plain'); // Plain text (YAML) - res.end(content); - } catch (e) { - res.statusCode = 404; - res.end(JSON.stringify({ error: 'File not found' })); - } - return; - } - - if (req.method === 'POST') { - let body = ''; - req.on('data', chunk => body += chunk); - req.on('end', () => { - try { - fs.writeFileSync(filePath, body, 'utf-8'); - res.statusCode = 200; - res.end(JSON.stringify({ success: true })); - } catch (e: any) { - res.statusCode = 500; - res.end(JSON.stringify({ error: e.message })); - } - }); - return; - } - } - - if (req.url?.startsWith('/api/schema/find')) { - const urlObj = new URL(req.url, `http://${req.headers.host}`); - const objectName = urlObj.searchParams.get('object'); - - if (!objectName) { - res.statusCode = 400; - res.end(JSON.stringify({ error: 'Missing object parameter' })); - return; - } - - try { - // Find all object.yml files - const files = await glob('**/*.object.yml', { - cwd: rootDir, - ignore: ['node_modules/**', 'dist/**', 'build/**', 'out/**', '.git/**', '.next/**'] - }); - let foundFile = null; - - // Naive parsing to find the object definition - // We don't use the FULL parser, just checks if "name: objectName" is present - for (const file of files) { - const content = fs.readFileSync(path.join(rootDir, file), 'utf-8'); - // Simple check: name: or name: "" - // This creates a regex that looks for `name:` followed by the objectName - // Handles spaces, quotes - const regex = new RegExp(`^\\s*name:\\s*["']?${objectName}["']?\\s*$`, 'm'); - if (regex.test(content)) { - foundFile = file; - break; - } - } - - if (foundFile) { - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ file: foundFile })); - } else { - res.statusCode = 404; - res.end(JSON.stringify({ error: 'Object definition file not found' })); - } - } catch (e: any) { - res.statusCode = 500; - res.end(JSON.stringify({ error: e.message })); - } - return; - } - - if (req.url?.startsWith('/api/metadata')) { - return metadataHandler(req, res); - } - - if (req.url?.startsWith('/api')) { - // Strip /api prefix if needed by the handler, - // but ObjectQL node handler usually expects full path or depends on internal routing. - // Actually createNodeHandler handles /objectql/v1/ etc? - // Let's assume standard behavior: pass to handler - return nodeHandler(req, res); - } - - // Redirect root to studio - if (req.url === '/') { - res.writeHead(302, { 'Location': '/studio' }); - res.end(); - return; - } - - res.statusCode = 404; - res.end('Not Found'); - }); - - const tryListen = (port: number) => { - server.removeAllListeners('error'); - server.removeAllListeners('listening'); // Prevent stacking callbacks - - server.on('error', (e: any) => { - if (e.code === 'EADDRINUSE') { - if (port - startPort < 10) { - console.log(chalk.yellow(`Port ${port} is in use, trying ${port + 1}...`)); - server.close(); - tryListen(port + 1); - } else { - console.error(chalk.red(`❌ Unable to find a free port.`)); - process.exit(1); - } - } else { - console.error(chalk.red('❌ Server error:'), e); - } - }); - - server.listen(port, () => { - const url = `http://localhost:${port}/studio`; - console.log(chalk.green(`\n🚀 Studio running at: ${chalk.bold(url)}`)); - console.log(chalk.gray(` API endpoint: http://localhost:${port}/api`)); - - if (options.open) { - openBrowser(url); - } - }); - }; - - tryListen(startPort); -} diff --git a/packages/tools/cli/src/index.ts b/packages/tools/cli/src/index.ts index 6601a001..10a9d43a 100644 --- a/packages/tools/cli/src/index.ts +++ b/packages/tools/cli/src/index.ts @@ -8,7 +8,6 @@ import { build } from './commands/build'; import { test } from './commands/test'; import { lint } from './commands/lint'; import { format } from './commands/format'; -import { startStudio } from './commands/studio'; import { initProject } from './commands/init'; import { newMetadata } from './commands/new'; import { i18nExtract, i18nInit, i18nValidate } from './commands/i18n'; @@ -291,22 +290,6 @@ program await serve({ port: parseInt(options.port), dir: options.dir }); }); -// Studio command -program - .command('studio') - .alias('ui') - .description('Start the ObjectQL Studio') - .option('-p, --port ', 'Port to listen on', '5555') - .option('-d, --dir ', 'Directory containing schema', '.') - .option('--no-open', 'Do not open browser automatically') - .action(async (options) => { - await startStudio({ - port: parseInt(options.port), - dir: options.dir, - open: options.open - }); - }); - // AI command - Interactive by default, with specific subcommands for other modes const aiCmd = program .command('ai') diff --git a/packages/tools/studio/CHANGELOG.md b/packages/tools/studio/CHANGELOG.md deleted file mode 100644 index ca1becd6..00000000 --- a/packages/tools/studio/CHANGELOG.md +++ /dev/null @@ -1,89 +0,0 @@ -# @objectql/studio - -## 1.8.3 - -### Patch Changes - -- Release patch version 1.8.3 - - Small version update with latest improvements and bug fixes. - -## 1.8.2 - -### Patch Changes - -- Patch release v1.8.2 - Small version update with latest improvements - -## 1.8.1 - -### Patch Changes - -- Patch release with documentation updates and bug fixes - -## 1.8.0 - -### Minor Changes - -- Release minor version 1.8.0 - -## 1.7.3 - -### Patch Changes - -- Release patch version 1.7.3 with latest improvements and bug fixes - -## 1.7.2 - -### Patch Changes - -- Release patch version 1.7.2 - -## 1.7.1 - -### Patch Changes - -- Release small version update with latest improvements - -## 1.7.0 - -### Minor Changes - -- Release version 1.7.0 with improvements and bug fixes: - - Updated default port for ObjectQL Studio to 5555 - - Improved port listening logic in Studio - - Enhanced stability and performance - -## 1.6.1 - -## 1.6.0 - -### Minor Changes - -- Minor version release - 1.6.0 - -## 1.5.0 - -### Minor Changes - -- Minor version release - 1.5.0 - -## 1.4.0 - -### Minor Changes - -- Release version 1.4.0 with new features and enhancements: - - Added complete REST API implementation with CRUD operations - - Enhanced error handling with standardized error codes and HTTP status mapping - - Added AI context support for tracking intent and use cases - - Enhanced metadata API with detailed field information and action listing - - Improved JSON-RPC API with better error categorization - - Added hooks and actions validation and implementation - - Updated documentation and examples - -## 1.3.1 - -## 0.2.0 - -### Minor Changes - -- 7df2977: 拆分 objectos diff --git a/packages/tools/studio/README.md b/packages/tools/studio/README.md deleted file mode 100644 index ed5e0114..00000000 --- a/packages/tools/studio/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# @objectql/studio - -Web-based admin studio for ObjectQL database management. - -## Features - -- **Object Browser**: View all registered objects/tables in your database -- **Data Grid**: Browse and search records with pagination -- **CRUD Operations**: Create, read, update, and delete records -- **Schema Inspector**: View object definitions and field metadata -- **Modern UI**: Clean, responsive design with intuitive navigation - -## Usage - -The console is typically served alongside your ObjectQL server: - -```typescript -import express from 'express'; -import { ObjectQL } from '@objectql/core'; -import { createNodeHandler, createStudioHandler } from '@objectql/server'; - -const app = new ObjectQL({ /* ... */ }); -const server = express(); - -// ... setup objectql ... - -// Serve the Studio -server.get('/studio*', createStudioHandler()); -``` - -// API endpoints -server.all('/api/objectql', createNodeHandler(app)); - -// Serve console UI -server.use('/console', serveConsole()); - -server.listen(3004); -``` - -Then visit `http://localhost:3004/console` in your browser. - -## Development - -```bash -pnpm install -pnpm run dev -``` diff --git a/packages/tools/studio/index.html b/packages/tools/studio/index.html deleted file mode 100644 index 77e4e9c3..00000000 --- a/packages/tools/studio/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - ObjectQL Console - - -
- - - diff --git a/packages/tools/studio/package.json b/packages/tools/studio/package.json deleted file mode 100644 index 2ddc8cad..00000000 --- a/packages/tools/studio/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@objectql/studio", - "version": "1.8.3", - "description": "Web-based admin studio for ObjectQL - Visual database management, schema editor, and data browser", - "keywords": [ - "objectql", - "studio", - "admin", - "gui", - "database", - "management", - "react", - "vite", - "ag-grid", - "visual-editor" - ], - "license": "MIT", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" - }, - "dependencies": { - "ag-grid-community": "^31.0.0", - "ag-grid-react": "^31.0.0", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.0", - "lucide-react": "^0.300.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.20.0", - "tailwind-merge": "^2.2.0", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@types/node": "^20.10.0", - "@types/react": "^18.2.43", - "@types/react-dom": "^18.2.17", - "@vitejs/plugin-react": "^4.2.1", - "autoprefixer": "^10.4.16", - "postcss": "^8.4.32", - "tailwindcss": "^3.4.0", - "typescript": "^5.3.0", - "vite": "^5.0.8" - } -} diff --git a/packages/tools/studio/postcss.config.cjs b/packages/tools/studio/postcss.config.cjs deleted file mode 100644 index 96bb01e7..00000000 --- a/packages/tools/studio/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} \ No newline at end of file diff --git a/packages/tools/studio/src/App.css b/packages/tools/studio/src/App.css deleted file mode 100644 index c07731f5..00000000 --- a/packages/tools/studio/src/App.css +++ /dev/null @@ -1,308 +0,0 @@ -.app { - display: flex; - flex-direction: column; - min-height: 100vh; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 0 20px; - width: 100%; -} - -/* Header */ -.app-header { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 1rem 0; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); -} - -.app-header .container { - display: flex; - justify-content: space-between; - align-items: center; -} - -.logo { - font-size: 1.5rem; - font-weight: 700; - letter-spacing: -0.5px; -} - -.logo a { - color: white; - text-decoration: none; - transition: opacity 0.2s; -} - -.logo a:hover { - opacity: 0.9; -} - -.nav { - display: flex; - gap: 1.5rem; -} - -.nav-link { - color: white; - text-decoration: none; - padding: 0.5rem 1rem; - border-radius: 6px; - transition: background 0.2s; - font-weight: 500; -} - -.nav-link:hover { - background: rgba(255, 255, 255, 0.15); -} - -/* Main */ -.app-main { - flex: 1; - padding: 2rem 0; -} - -/* Footer */ -.app-footer { - background: #2d3748; - color: #a0aec0; - padding: 1.5rem 0; - text-align: center; - font-size: 0.875rem; -} - -/* Card Component */ -.card { - background: white; - border-radius: 8px; - padding: 1.5rem; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - margin-bottom: 1.5rem; -} - -.card-title { - font-size: 1.25rem; - font-weight: 600; - margin-bottom: 1rem; - color: #2d3748; -} - -/* Button */ -.btn { - padding: 0.5rem 1rem; - border: none; - border-radius: 6px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - text-decoration: none; - display: inline-block; - font-size: 0.875rem; -} - -.btn-primary { - background: #667eea; - color: white; -} - -.btn-primary:hover { - background: #5568d3; - transform: translateY(-1px); - box-shadow: 0 4px 6px rgba(102, 126, 234, 0.3); -} - -.btn-secondary { - background: #e2e8f0; - color: #2d3748; -} - -.btn-secondary:hover { - background: #cbd5e0; -} - -.btn-danger { - background: #f56565; - color: white; -} - -.btn-danger:hover { - background: #e53e3e; -} - -.btn-sm { - padding: 0.375rem 0.75rem; - font-size: 0.8125rem; -} - -/* Table */ -.table-container { - overflow-x: auto; - margin-top: 1rem; -} - -.table { - width: 100%; - border-collapse: collapse; - background: white; -} - -.table th, -.table td { - padding: 0.75rem 1rem; - text-align: left; - border-bottom: 1px solid #e2e8f0; -} - -.table th { - background: #f7fafc; - font-weight: 600; - color: #2d3748; - font-size: 0.875rem; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.table tr:hover { - background: #f7fafc; -} - -.table td { - color: #4a5568; -} - -/* Form */ -.form-group { - margin-bottom: 1rem; -} - -.form-label { - display: block; - margin-bottom: 0.5rem; - font-weight: 500; - color: #2d3748; - font-size: 0.875rem; -} - -.form-input, -.form-textarea, -.form-select { - width: 100%; - padding: 0.5rem 0.75rem; - border: 1px solid #e2e8f0; - border-radius: 6px; - font-size: 0.875rem; - transition: border-color 0.2s; -} - -.form-input:focus, -.form-textarea:focus, -.form-select:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} - -.form-textarea { - min-height: 100px; - resize: vertical; -} - -/* Grid */ -.grid { - display: grid; - gap: 1rem; -} - -.grid-2 { - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); -} - -/* Alert */ -.alert { - padding: 1rem; - border-radius: 6px; - margin-bottom: 1rem; -} - -.alert-info { - background: #e6f7ff; - color: #0050b3; - border-left: 4px solid #1890ff; -} - -.alert-error { - background: #fff1f0; - color: #cf1322; - border-left: 4px solid #f5222d; -} - -.alert-success { - background: #f6ffed; - color: #389e0d; - border-left: 4px solid #52c41a; -} - -/* Loading */ -.loading { - text-align: center; - padding: 3rem; - color: #a0aec0; -} - -.spinner { - display: inline-block; - width: 40px; - height: 40px; - border: 4px solid #e2e8f0; - border-top-color: #667eea; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -/* Breadcrumb */ -.breadcrumb { - display: flex; - gap: 0.5rem; - margin-bottom: 1.5rem; - font-size: 0.875rem; - color: #718096; -} - -.breadcrumb a { - color: #667eea; - text-decoration: none; -} - -.breadcrumb a:hover { - text-decoration: underline; -} - -.breadcrumb-separator { - color: #cbd5e0; -} - -/* Badge */ -.badge { - display: inline-block; - padding: 0.25rem 0.5rem; - border-radius: 4px; - font-size: 0.75rem; - font-weight: 500; -} - -.badge-primary { - background: #667eea; - color: white; -} - -.badge-secondary { - background: #e2e8f0; - color: #2d3748; -} diff --git a/packages/tools/studio/src/App.tsx b/packages/tools/studio/src/App.tsx deleted file mode 100644 index c6682ed9..00000000 --- a/packages/tools/studio/src/App.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom'; -import { Sidebar } from '@/components/Sidebar'; -import { Dashboard } from '@/pages/Dashboard'; -import { ObjectView } from '@/pages/ObjectView'; -import { SchemaEditor } from '@/pages/SchemaEditor'; -import { MetadataBrowser } from '@/pages/MetadataBrowser'; -import './index.css'; - -// Wrapper to extract params -function ObjectViewWrapper() { - const { name } = useParams(); - if (!name) return null; - return ; -} - -function App() { - return ( - -
- -
- - } /> - } /> - } /> - } /> - } /> - -
-
-
- ); -} - -export default App; - diff --git a/packages/tools/studio/src/components/DataGrid.tsx b/packages/tools/studio/src/components/DataGrid.tsx deleted file mode 100644 index 4f98a37a..00000000 --- a/packages/tools/studio/src/components/DataGrid.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useParams, Link, useNavigate } from 'react-router-dom'; - -interface Record { - _id?: string; - id?: string; - [key: string]: any; -} - -function DataGrid() { - const { objectName } = useParams<{ objectName: string }>(); - const navigate = useNavigate(); - const [records, setRecords] = useState([]); - const [fields, setFields] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [showCreateForm, setShowCreateForm] = useState(false); - - useEffect(() => { - fetchRecords(); - fetchObjectMetadata(); - }, [objectName]); - - const fetchObjectMetadata = async () => { - try { - const response = await fetch(`/api/metadata/object/${objectName}`); - if (response.ok) { - const data = await response.json(); - if (data.fields) { - const fieldsArray = Array.isArray(data.fields) ? data.fields : Object.values(data.fields); - setFields(fieldsArray.map((f: any) => f.name || f)); - } - } - } catch (e) { - console.error('Failed to fetch metadata:', e); - } - }; - - const fetchRecords = async () => { - try { - setLoading(true); - const response = await fetch('/api/objectql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - op: 'find', - object: objectName, - args: { limit: 100 } - }) - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - const data = result.items || result.data || []; - setRecords(data); - - // Auto-detect fields if not already set - if (data.length > 0 && fields.length === 0) { - const sampleRecord = data[0]; - const detectedFields = Object.keys(sampleRecord).filter( - k => !k.startsWith('_') || k === '_id' - ); - setFields(detectedFields); - } - } catch (e: any) { - console.error('Failed to fetch records:', e); - setError(e.message); - } finally { - setLoading(false); - } - }; - - const handleCreate = async (formData: Record) => { - try { - const response = await fetch('/api/objectql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - op: 'create', - object: objectName, - args: { data: formData } - }) - }); - - if (!response.ok) { - throw new Error('Failed to create record'); - } - - setShowCreateForm(false); - fetchRecords(); - } catch (e: any) { - alert(`Error: ${e.message}`); - } - }; - - const formatValue = (value: any): string => { - if (value === null || value === undefined) return '-'; - if (typeof value === 'object') return JSON.stringify(value); - if (typeof value === 'boolean') return value ? 'Yes' : 'No'; - return String(value); - }; - - const getRecordId = (record: Record): string => { - return record._id || record.id || ''; - }; - - if (loading) { - return ( -
-
-

Loading records...

-
- ); - } - - return ( -
-
- Objects - / - {objectName} -
- -
-
-

{objectName}

- -
- - {error && ( -
- Error: {error} -
- )} - - {showCreateForm && ( - setShowCreateForm(false)} - /> - )} - - {records.length === 0 ? ( -
- No records found. Click "New Record" to create one. -
- ) : ( -
- - - - {fields.map((field) => ( - - ))} - - - - - {records.map((record, idx) => ( - - {fields.map((field) => ( - - ))} - - - ))} - -
{field}Actions
{formatValue(record[field])} - -
-
- )} - -
- Showing {records.length} record{records.length !== 1 ? 's' : ''} -
-
-
- ); -} - -function CreateRecordForm({ fields, onSubmit, onCancel }: { - fields: string[]; - onSubmit: (data: Record) => void; - onCancel: () => void; -}) { - const [formData, setFormData] = useState({}); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - onSubmit(formData); - }; - - return ( -
-

Create New Record

- {fields.filter(f => f !== '_id' && f !== 'id').map((field) => ( -
- - setFormData({ ...formData, [field]: e.target.value })} - /> -
- ))} -
- - -
-
- ); -} - -export default DataGrid; diff --git a/packages/tools/studio/src/components/FileEditor.tsx b/packages/tools/studio/src/components/FileEditor.tsx deleted file mode 100644 index d82d1d0f..00000000 --- a/packages/tools/studio/src/components/FileEditor.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useState, useEffect } from 'react'; -import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Save } from 'lucide-react'; - -interface FileEditorProps { - filePath: string; - className?: string; - onSaveSuccess?: () => void; -} - -export function FileEditor({ filePath, className, onSaveSuccess }: FileEditorProps) { - const [content, setContent] = useState(''); - const [loading, setLoading] = useState(false); - const [saving, setSaving] = useState(false); - const [error, setError] = useState(null); - const [status, setStatus] = useState(null); - - useEffect(() => { - if (filePath) { - loadFile(filePath); - } - }, [filePath]); - - const loadFile = async (file: string) => { - setLoading(true); - setError(null); - try { - const res = await fetch(`/api/schema/content?file=${encodeURIComponent(file)}`); - if (!res.ok) throw new Error('Failed to load file'); - const text = await res.text(); - setContent(text); - } catch (e) { - console.error(e); - setError('Failed to read file content'); - } finally { - setLoading(false); - } - }; - - const saveFile = async () => { - if (!filePath) return; - setSaving(true); - setStatus(null); - try { - const res = await fetch(`/api/schema/content?file=${encodeURIComponent(filePath)}`, { - method: 'POST', - body: content - }); - if (!res.ok) throw new Error('Failed to save'); - setStatus('File saved successfully!'); - setTimeout(() => setStatus(null), 3000); - if (onSaveSuccess) onSaveSuccess(); - } catch (e) { - setError('Failed to save file'); - } finally { - setSaving(false); - } - }; - - return ( - - - - {filePath} - -
- {status && {status}} - {error && {error}} - -
-
- - {loading && ( -
-
-
- )} -