--- url: /get-started/how-nocobase-works.md --- # How NocoBase Works --- url: /tutorials/v2/index.md --- # NocoBase 2.0 Beginner Tutorial This tutorial walks you through building a **minimal IT HelpDesk system** from scratch using NocoBase 2.0. The entire system requires only **2 data tables** and zero code, yet delivers ticket submission, category management, change tracking, access control, and a data dashboard. ## About This Tutorial - **Target audience**: Business users, technical users, or anyone interested in NocoBase (some computer background is recommended) - **Case project**: A minimal IT HelpDesk system with only 2 tables - **Estimated time**: 2-3 hours (non-technical), 1-1.5 hours (technical users) - **Prerequisites**: Docker environment or [Online Demo](https://demo.nocobase.com/new) (24-hour trial, no installation needed) - **Version**: NocoBase 2.0 ## What You'll Learn Through 7 hands-on chapters, you'll master the core concepts and workflow of NocoBase: | # | Chapter | Key Topics | |---|---------|------------| | 1 | [Getting Started — Up and Running in 5 Minutes](./01-getting-started/) | Docker install, UI Editor vs Usage mode | | 2 | [Data Modeling — Building the Skeleton](./02-data-modeling/) | Collections, Fields, Relations | | 3 | [Building Pages — Making Data Visible](./03-building-pages/) | Blocks, Table block, Filtering & Sorting | | 4 | [Forms & Details — Entering Data](./04-forms-and-details/) | Form blocks, Field linkage | | 5 | [Users & Permissions — Who Sees What](./05-roles-and-permissions/) | Roles, Menu permissions, Data permissions | | 6 | [Workflows — Automation](./06-workflows/) | Notifications, Triggers | | 7 | [Dashboard — The Big Picture](./07-dashboard/) | Charts, Markdown blocks | ## Data Model Preview This tutorial is built around a minimal data model — just **2 tables**, but enough to cover data modeling, page building, form design, access control, workflows, and dashboards. | Table | Key Fields | |-------|-----------| | Tickets | Title, Description, Status, Priority | | Categories | Name, Color | ## FAQ ### What is NocoBase best suited for? Internal business tools, data management systems, approval workflows, CRM, ERP, and other scenarios requiring flexible customization with self-hosted deployment. ### What prerequisites do I need? No programming required, but some basic computer knowledge is recommended. The tutorial explains concepts like tables, fields, and relationships step by step. Experience with databases or spreadsheets is a plus. ### Can the tutorial system be extended? Yes. This tutorial uses only 2 tables, but NocoBase supports complex multi-table relationships, external API integrations, and custom plugins. ### What deployment environment is needed? Docker is recommended (Docker Desktop or Linux server), minimum 2 cores and 4GB RAM. Git source installation is also supported. For learning purposes, you can also request an [Online Demo](https://demo.nocobase.com/new) — no installation needed, valid for 24 hours. ### Are there limitations in the free version? Core features are fully free and open-source. The commercial edition offers additional premium plugins and technical support. See [commercial pricing](https://www.nocobase.com/en/commercial) for details. ## Tech Stack NocoBase 2.0 is built on: - **Frontend**: React + [Ant Design](https://ant.design/) 5.0 - **Backend**: Node.js + Koa - **Database**: PostgreSQL (also supports [MySQL](/get-started/installation/docker), MariaDB) - **Deployment**: [Docker](/get-started/installation/docker), Kubernetes ## Platform Comparison If you're evaluating no-code/low-code platforms: | Platform | Highlights | Difference from NocoBase | |----------|-----------|--------------------------| | [Appsmith](https://www.appsmith.com/) | Open-source, strong frontend customization | NocoBase is more data-model driven | | [Retool](https://retool.com/) | Internal tool platform | NocoBase is fully open-source, no usage limits | | [Airtable](https://airtable.com/) | Online collaborative database | NocoBase supports self-hosted deployment | | [Budibase](https://budibase.com/) | Open-source low-code, self-hostable | NocoBase has stronger plugin architecture | ## Related Docs ### Getting Started - [How NocoBase Works](/get-started/how-nocobase-works) — Core concepts overview - [Quick Start](/get-started/quickstart) — Installation and initial setup - [System Requirements](/get-started/system-requirements) — Hardware and software requirements ### More Tutorials - [NocoBase 1.x Tutorials](/tutorials/v1/) — Task management system tutorial with advanced topics ### Solutions - [Ticket System Solution](/solution/ticket-system/) — AI-powered intelligent ticket management - [CRM Solution](/solution/crm/) — Customer relationship management - [AI Employees](/ai-employees/quick-start) — Add AI capabilities to your system Ready? Let's start with [Chapter 1: Getting Started](./01-getting-started/)! --- url: /tutorials/v2/01-getting-started.md --- # Chapter 1: Getting Started — Build a Working System in 5 Minutes In this series, we'll build a **minimal IT HelpDesk system** from scratch using NocoBase. The entire system needs only **2 [data tables](/data-sources/main/collection)** and zero lines of code — yet it will support ticket submission, category management, change tracking, access control, and even a [dashboard](/data-visualization). This chapter walks you through deploying NocoBase with [Docker](/get-started/installation/docker), completing your first login, and understanding the difference between [UI Editor mode and Usage mode](/get-started/how-nocobase-works). ## 1.1 What Is NocoBase Have you ever been in one of these situations? - Your team needs an internal system, but off-the-shelf software never quite fits - Hiring developers for a custom build is too expensive and too slow, and requirements keep changing - You're using spreadsheets as a workaround, but the data keeps getting messier **NocoBase was built to solve this problem.** It's an open-source, highly extensible **AI-powered no-code development platform**. You can build your own business systems through configuration and drag-and-drop — no coding required. Compared to other no-code tools, NocoBase has a few core principles: - **Data model driven**: Define your data structure first, then use [blocks](/interface-builder/blocks) to display data, then [actions](/interface-builder/actions) to process it — UI and data are fully decoupled - **WYSIWYG**: [Pages](/interface-builder/pages) are your canvas. Click anywhere to edit, as intuitive as building a Notion page - **Everything is a plugin**: All features are [plugins](/development/plugin), similar to WordPress — install what you need - **AI built into your workflow**: Built-in [AI employees](/ai-employees/quick-start) that can perform analysis, translation, data entry, and more - **Open source + self-hosted**: Core code is fully open source, all data stays on your own server ## 1.2 Installing NocoBase NocoBase supports multiple installation methods. We'll go with the simplest: **Docker**. ### Prerequisites You need [Docker](https://docs.docker.com/get-docker/) and Docker Compose installed on your machine, with the Docker service running. Windows, Mac, and Linux are all supported. ### Step 1: Download the Configuration File Open your terminal (PowerShell on Windows, Terminal on Mac) and run: ```bash # Create a project directory and enter it mkdir my-project && cd my-project # Download docker-compose.yml (defaults to PostgreSQL) curl -fsSL https://static-docs.nocobase.com/docker-compose/en/latest-postgres.yml -o docker-compose.yml ``` > **Other databases?** Replace `postgres` in the URL with `mysql` or `mariadb`. > You can also choose different versions: `latest` (stable), `beta` (testing), or `alpha` (development). See the [official installation docs](https://docs.nocobase.com/get-started/installation/docker) for details. > > | Database | Download URL | > |----------|-------------| > | PostgreSQL (recommended) | `https://static-docs.nocobase.com/docker-compose/en/latest-postgres.yml` | > | MySQL | `https://static-docs.nocobase.com/docker-compose/en/latest-mysql.yml` | > | MariaDB | `https://static-docs.nocobase.com/docker-compose/en/latest-mariadb.yml` | ### Step 2: Start It Up ```bash # Pull images docker compose pull # Start in background (first run will auto-install) docker compose up -d # Watch logs to confirm successful startup docker compose logs -f app ``` When you see this line in the output, you're good to go: ``` 🚀 NocoBase server running at: http://localhost:13000/ ``` ![01-getting-started-2026-03-11-07-49-19](https://static-docs.nocobase.com/01-getting-started-2026-03-11-07-49-19.png) ### Step 3: Log In Open your browser and go to `http://localhost:13000`. Log in with the default credentials: - **Email**: `admin@nocobase.com` - **Password**: `admin123` > Remember to change the default password after your first login. ## 1.3 Getting to Know the Interface After logging in, you'll see a clean initial interface. Don't worry about it being empty — let's first understand two key concepts. ### UI Editor Mode vs Usage Mode NocoBase has two interface modes: | Mode | Description | Who Uses It | |------|-------------|-------------| | **Usage Mode** | The everyday interface for regular users | Everyone | | **UI Editor Mode** | The design mode for building and tweaking the interface | Admins | To switch: click the **"UI Editor"** button in the top-right corner (a highlighter pen icon). ![01-getting-started-2026-03-11-08-17-26](https://static-docs.nocobase.com/01-getting-started-2026-03-11-08-17-26.png) When you enable UI Editor mode, you'll notice **orange highlight borders** appearing around many elements on the page — this means they're configurable. Each configurable element shows a small icon in its top-right corner; click it to access its settings. Here's what it looks like on a demo system: ![01-getting-started-2026-03-11-08-19-24](https://static-docs.nocobase.com/01-getting-started-2026-03-11-08-19-24.png) As shown above: [menus](/interface-builder/menus), table action bars, and the bottom of the page all show orange indicators. Click them to create or configure elements. > **Remember this pattern**: In NocoBase, whenever you want to modify something on the page, enter UI Editor mode, find the small icon in its top-right corner, and click it. ### Basic Interface Layout The NocoBase interface is composed of three areas: ``` ┌──────────────────────────────────────────┐ │ Top Navigation Bar │ ├──────────┬───────────────────────────────┤ │ │ │ │ Left │ Content Area │ │ Sidebar │ (place blocks here) │ │ (groups) │ │ │ │ │ └──────────┴───────────────────────────────┘ ``` - **Top Navigation Bar**: Houses top-level menus for switching between modules - **Left Sidebar (groups)**: If using group menus, this shows second-level navigation to organize page hierarchy - **Content Area**: The main body of the page, where you place various **Blocks** to display and interact with data ![01-getting-started-2026-03-11-08-24-34](https://static-docs.nocobase.com/01-getting-started-2026-03-11-08-24-34.png) It's still empty for now — but starting from the next chapter, we'll fill it up. ## 1.4 What We're Going to Build Over the course of this tutorial, we'll build an **IT HelpDesk system** step by step. It will support: - ✅ Ticket submission: Users fill in title, description, category, and priority - ✅ Ticket list: Filter by status or category at a glance - ✅ Access control: Regular [users](/users-permissions/user) see only their own tickets; admins see everything - ✅ Dashboard: Real-time statistics on ticket distribution and trends - ✅ Audit log (built-in) The entire system needs just **2 data tables**: | Table | Purpose | Custom Fields | |-------|---------|---------------| | Categories | Ticket categories (e.g., Network Issue, Software Bug) | 2 | | Tickets | The core table — each row is one ticket | 7-8 | That's right, just 2 tables. Common capabilities like users, [permissions](/users-permissions/role), file management, departments, email, and audit logs are all provided by built-in NocoBase plugins — no need to reinvent the wheel. We only need to focus on our business data. ## Summary In this chapter we: 1. Learned what NocoBase is — an open-source no-code platform 2. Installed and started NocoBase with Docker in one step 3. Understood the two interface modes (UI Editor / Usage) and the basic layout 4. Previewed the HelpDesk system we're going to build **Next chapter**: We'll get hands-on — enter the [Data Source Manager](/data-sources) and create our first data table. This is the skeleton of the entire system and NocoBase's most fundamental capability. See you in Chapter 2! ## Related Resources - [Docker Installation Guide](/get-started/installation/docker) — Full installation options and environment variables - [System Requirements](/get-started/system-requirements) — Hardware and software requirements - [How NocoBase Works](/get-started/how-nocobase-works) — Core concepts: data sources, blocks, actions --- url: /tutorials/v2/02-data-modeling.md --- # Chapter 2: Data Modeling — Two Tables for a Complete Ticket System In the last chapter, we installed NocoBase and got familiar with the interface. Now it's time to build the skeleton of our HelpDesk system — the **data model**. This chapter creates two [collections](/data-sources/main/collection) — Tickets and Categories — and configures [field types](/data-sources/field) ([single-line text](/data-sources/field/basic/input), [dropdown](/data-sources/field/choices/select), [many-to-one](/data-sources/field/associations/m2o) relations). The data model is the foundation: figure out what data you need and how it's related, then building pages and setting permissions becomes straightforward. ## 2.1 What Are Collections and Fields If you've used Excel before, this will feel familiar: | Excel Concept | NocoBase Concept | Description | |---------------|-----------------|-------------| | Worksheet | Collection | A container for one type of data | | Column header | Field | An attribute describing the data | | Each row | Record | One specific piece of data | ![02-data-modeling-2026-03-11-08-32-41](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-32-41.png) For example, our "Tickets" collection is like an Excel spreadsheet — each column is a field (Title, Status, Priority...), and each row is one ticket record. But NocoBase is much more powerful than Excel. It supports multiple **collection types**, each with different built-in capabilities: | Type | Best For | Examples | |------|----------|----------| | **General** | Most business data | Tickets, Orders, Customers | | **Tree** | Hierarchical data | Category trees, Org charts | | Calendar | Date-based events | Meetings, Schedules | | File | Attachment management | Documents, Images | Today we'll use **General** and **Tree** collections. We'll cover the others when needed. **Enter Data Source Manager**: Click the **"Data Source Manager"** icon in the bottom-left corner (the database icon next to the gear). You'll see the "Main [data source](/data-sources)" — this is where all our tables live. ![02-data-modeling-2026-03-11-08-35-08](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-35-08.png) ## 2.2 Creating the Core Table: Tickets Let's jump right in and create the heart of our system — the Tickets table. ### Create the Table 1. On the Data Source Manager page, click **"Main data source"** to enter ![02-data-modeling-2026-03-11-08-36-06](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-36-06.png) 2. Click **"Create collection"**, then select **"General collection"** ![02-data-modeling-2026-03-11-08-38-52](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-38-52.png) 3. Collection name: `tickets`, Display name: `Tickets` ![02-data-modeling-2026-03-11-08-40-34](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-40-34.png) When creating a table, the system checks a set of **system fields** by default. These automatically track metadata for every record: | Field | Description | |-------|-------------| | ID | Primary key, unique identifier | | Created at | When the record was created | | Created by | Who created the record | | Last updated at | When it was last modified | | Last updated by | Who last modified it | Keep these defaults as-is — no manual management needed. You can uncheck them if a specific scenario doesn't need them. ### Adding Basic Fields The table is created. Now let's add fields. Click **"Configure fields"** on the Tickets table, and you'll see the default system fields already listed. ![02-data-modeling-2026-03-11-08-58-48](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-58-48.png) ![02-data-modeling-2026-03-11-08-59-47](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-08-59-47.png) Click the **"Add field"** button in the top-right corner to expand a dropdown of field types — pick the one you want to add. ![02-data-modeling-2026-03-11-09-00-22](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-00-22.png) We'll add the ticket's own fields first; relation fields come later. **1. Title (Single line text)** Every ticket needs a short title to summarize the issue. Click **"Add field"** → select **"Single line text"**: ![02-data-modeling-2026-03-11-09-01-00](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-01-00.png) - Field name: `title`, Display name: `Title` - Click **"Set validation rules"**, add a **"Required"** rule ![02-data-modeling-2026-03-11-09-02-40](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-02-40.png) **2. Description (Markdown(Vditor))** For detailed problem descriptions with rich formatting — images, code blocks, etc. Under **"Add field"** → **"Media"** category, you'll find three options: | Field Type | Features | |-----------|----------| | Markdown | Basic Markdown, simple styling | | Rich Text | Rich text editor with attachment uploads | | **Markdown(Vditor)** | Most feature-rich: WYSIWYG, instant rendering, and source code editing modes | We'll go with **Markdown(Vditor)**. ![02-data-modeling-2026-03-11-09-09-58](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-09-58.png) - Field name: `description`, Display name: `Description` ![02-data-modeling-2026-03-11-09-10-50](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-10-50.png) **3. Status (Single select)** ![02-data-modeling-2026-03-11-09-12-00](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-12-00.png) Tickets go through stages from submission to completion, so we need a status field to track progress. - Field name: `status`, Display name: `Status` - Add option values (each option needs a "Value" and "Label"; color is optional): | Value | Label | Color | |-------|-------|-------| | pending | Pending | Orange | | in_progress | In Progress | Blue | | completed | Completed | Green | ![02-data-modeling-2026-03-11-09-17-44](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-17-44.png) Fill in the options and save first. Then click **"Edit"** on this field again — now you can set the "Default value" to **"Pending"**. ![02-data-modeling-2026-03-11-09-20-28](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-20-28.png) ![02-data-modeling-2026-03-11-09-22-34](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-22-34.png) > The first time you create the field, there are no options yet, so you can't pick a default value — you need to save first, then come back to set it. > Why a single select? Because status is a fixed set of values. A dropdown prevents users from entering arbitrary text, keeping data clean. **4. Priority (Single select)** Helps distinguish urgency so the team can sort and tackle tickets efficiently. - Field name: `priority`, Display name: `Priority` - Add option values: | Value | Label | Color | |-------|-------|-------| | low | Low | | | medium | Medium | | | high | High | Orange | | urgent | Urgent | Red | At this point, the Tickets table has 4 basic fields. But — shouldn't a ticket have a "category"? Like "Network Issue" or "Software Bug"? We could make Category a dropdown, but you'd quickly run into a problem: categories can have sub-categories ("Hardware" → "Monitor", "Keyboard", "Printer"), and dropdowns can't handle that. We need **a separate table** for categories. And NocoBase's **Tree collection** is perfect for this. ## 2.3 Creating the Categories Tree Table ### What Is a Tree Collection A tree collection is a special type of table with built-in **parent-child relationships** — every record can have a parent node. This is ideal for hierarchical data: ``` Hardware ← Level 1 ├── Monitor ← Level 2 ├── Keyboard & Mouse └── Printer Software ├── Office Apps └── System Issues Network Account ``` With a general collection, you'd have to manually create a "Parent Category" field to build this hierarchy. A **tree collection handles it automatically** and supports tree views, adding child records, and more. ### Create the Table 1. Go back to Data Source Manager, click **"Create collection"** 2. This time, select **"Tree collection"** (not General!) ![02-data-modeling-2026-03-11-09-26-07](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-26-07.png) 3. Collection name: `categories`, Display name: `Categories` ![02-data-modeling-2026-03-11-09-26-55](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-26-55.png) > After creation, you'll notice the table has two extra relation fields — **"Parent"** and **"Children"** — beyond the standard system fields. This is the tree collection's special power. Use Parent to access the parent node and Children to access all child nodes, without any manual setup. ![02-data-modeling-2026-03-11-09-27-40](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-27-40.png) ### Add Fields Click **"Configure fields"** to enter the field list. You'll see the system fields plus the auto-generated Parent and Children fields. Click **"Add field"** in the top-right: **Field 1: Category Name** 1. Select **"Single line text"** 2. Field name: `name`, Display name: `Name` 3. Click **"Set validation rules"**, add a **"Required"** rule **Field 2: Color** 1. Select **"Color"** 2. Field name: `color`, Display name: `Color` ![02-data-modeling-2026-03-11-09-28-59](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-28-59.png) The Color field gives each category its own visual identity — it will make the interface much more intuitive later. ![02-data-modeling-2026-03-11-09-29-23](https://static-docs.nocobase.com/02-data-modeling-2026-03-11-09-29-23.png) With that, both tables' basic fields are configured. Now let's link them together. ## 2.4 Back to Tickets: Adding Relation Fields > **Relation fields can be a bit abstract at first.** If it doesn't click right away, feel free to skip ahead to [Chapter 3: Building Pages](./03-building-pages/) and see how data is displayed in practice, then come back here to add the relation fields. Tickets need to be linked to a category, a submitter, and an assignee. These are called **relation fields** — instead of storing text directly (like "Title" does), they store the ID of a record in another table, and use that ID to look up the corresponding record. Let's look at a specific ticket — on the left are the ticket's attributes. "Category" and "Submitter" don't store text; they store an ID. The system uses that ID to find the exact matching record from the tables on the right: ![02-data-modeling-2026-03-12-00-50-10](https://static-docs.nocobase.com/02-data-modeling-2026-03-12-00-50-10.png) On the interface, you see names like "Network" and "Alice", but behind the scenes it's all connected by IDs. **Multiple tickets can point to the same category or the same user** — this relationship is called **[Many-to-one](/data-sources/field/associations/m2o)**. ### Adding Relation Fields Go back to Tickets → "Configure fields" → "Add field", select **"Many to one"**. ![02-data-modeling-2026-03-12-00-52-39](https://static-docs.nocobase.com/02-data-modeling-2026-03-12-00-52-39.png) You'll see these configuration options: | Option | Description | How to Fill | |--------|-------------|-------------| | Source collection | Current table (auto-filled) | Don't change | | **Target collection** | Which table to link to | Select the target | | **Foreign key** | The linking column stored in the current table | Enter a meaningful name | | Target collection key field | Defaults to `id` | Keep as-is | | ON DELETE | What happens when the target record is deleted | Keep as-is | ![02-data-modeling-2026-03-12-00-58-38](https://static-docs.nocobase.com/02-data-modeling-2026-03-12-00-58-38.png) > The foreign key defaults to a random name like `f_xxxxx`. We recommend changing it to something meaningful for easier maintenance. Use lowercase with underscores (e.g., `category_id`) instead of camelCase. Add the following three fields: **5. Category → Categories table** - Display name: `Category` - Target collection: Select **"Categories"** (if not in the list, type the name and it will be auto-created) - Foreign key: `category_id` **6. Submitter → Users table** Records who submitted this ticket. NocoBase has a built-in Users table — just link to it. - Display name: `Submitter` - Target collection: Select **"Users"** - Foreign key: `submitter_id` ![02-data-modeling-2026-03-12-01-00-09](https://static-docs.nocobase.com/02-data-modeling-2026-03-12-01-00-09.png) **7. Assignee → Users table** Records who is responsible for handling this ticket. - Display name: `Assignee` - Target collection: Select **"Users"** - Foreign key: `assignee_id` ![02-data-modeling-2026-03-12-01-00-22](https://static-docs.nocobase.com/02-data-modeling-2026-03-12-01-00-22.png) ## 2.5 The Complete Data Model Let's review the full data model we've built: ![02-data-modeling-2026-03-16-00-30-35](https://static-docs.nocobase.com/02-data-modeling-2026-03-16-00-30-35.png) `}o--||` represents a many-to-one relationship: "many" on the left, "one" on the right. ## Summary In this chapter we completed the data modeling — the entire skeleton of our HelpDesk system: 1. **Tickets** (`tickets`): 4 basic fields + 3 relation fields, created as a **General collection** 2. **Categories** (`categories`): 2 custom fields + auto-generated Parent/Children fields, created as a **Tree collection** with built-in hierarchy support Key concepts we learned: - **Collection** = A container for one type of data - **Collection types** = Different types for different scenarios (General, Tree, etc.) - **Field** = A data attribute, created via "Configure fields" → "Add field" - **System fields** = ID, Created at, Created by, etc. — auto-checked when creating a table - **Relation field (Many-to-one)** = Points to a record in another table, linking tables together > You may notice that later screenshots already contain data — we pre-loaded test data for demonstration purposes. In NocoBase, all CRUD operations are done through the frontend pages. Chapter 3 covers building tables to display data, and Chapter 4 covers forms for data entry — stay tuned. ## Next Chapter Preview The skeleton is ready, but the tables are still empty. In the next chapter, we'll build pages to make the data visible. See you in Chapter 3! ## Related Resources - [Data Sources Overview](/data-sources) — Core data modeling concepts in NocoBase - [Field Types](/data-sources/field) — Complete field type reference - [Many-to-One Relations](/data-sources/field/associations/m2o) — Relationship configuration guide --- url: /tutorials/v2/03-building-pages.md --- # Chapter 3: Building Pages — From Blank to Functional In the last chapter, we built the skeleton of our data tables — but right now the data only lives in the "backend." Users can't see it at all. In this chapter, we'll bring our data **front and center**: create a [Table block](/interface-builder/blocks/data-blocks/table) to display ticket data, configure field visibility, sorting, [filtering](/interface-builder/blocks/filter-blocks/form), and pagination, turning it into a real, usable ticket list. ## 3.1 What Is a Block In NocoBase, a **Block** is a building brick on a page. Want to show a table? Drop in a Table block. Need a form? Add a Form block. A single page can freely combine multiple blocks, and you can drag and drop to rearrange the layout. Common block types: | Type | Purpose | |------|---------| | Table | Displays multiple records in rows and columns | | Form | Lets users input or edit data | | Details | Shows the full information of a single record | | Filter Form | Provides filter criteria to narrow down data in other blocks | | Chart | Pie charts, line charts, and other visualizations | | Markdown | A section of custom text or instructions | Remember this analogy: **Blocks = building bricks**. We're about to use them to assemble our tickets page. ## 3.2 Adding a Menu and Pages First, we need to create an entry point for "Tickets" in the system. 1. Click the **[UI Editor](/get-started/how-nocobase-works)** toggle in the top-right corner to enter design mode (the entire page will show orange editable borders). 2. Click the **"Add menu item"** button (`+` icon) in the top navigation bar, select **"Add group"**, and name it **"Tickets"**. 3. The "Tickets" [menu](/interface-builder/menus) appears immediately in the top navigation bar. **Click on it** — a sidebar menu will expand on the left. 4. In the sidebar, click the orange **"Add menu item"** button, select **"Modern page (v2)"**, and add two sub-[pages](/interface-builder/pages) one by one: - **All Tickets** — displays all tickets - **Categories** — manages category data ![03-building-pages-2026-03-12-09-38-36](https://static-docs.nocobase.com/03-building-pages-2026-03-12-09-38-36.png) ![03-building-pages-2026-03-12-09-41-26](https://static-docs.nocobase.com/03-building-pages-2026-03-12-09-41-26.png) > **Note**: You'll see both "Classic page (v1)" and "Modern page (v2)" options. This tutorial uses **v2** throughout. ## 3.3 Adding a Table Block Now go to the "All Tickets" page and add a Table block: 1. On the blank page, click **"Add block"**. 2. Select **Data blocks -> Table**. 3. In the [collection](/data-sources/main/collection) list that pops up, select **"Tickets"** (the table we created in the last chapter). ![03-building-pages-2026-03-13-08-44-07](https://static-docs.nocobase.com/03-building-pages-2026-03-13-08-44-07.png) Once the Table block is added, you'll see an empty table on the page. ![03-building-pages-2026-03-13-08-44-29](https://static-docs.nocobase.com/03-building-pages-2026-03-13-08-44-29.png) An empty table with no data isn't very useful for testing. Let's quickly add an "Add new" button so we can enter some test data: 1. Click **"Configure actions"** in the top-right corner of the table, and check **"Add new"**. ![03-building-pages-2026-03-17-14-58-39](https://static-docs.nocobase.com/03-building-pages-2026-03-17-14-58-39.png) 2. Click the new **"Add new"** button, then in the popup select **Add block → Form (Add New) → Current collection**. ![03-building-pages-2026-03-17-14-59-42](https://static-docs.nocobase.com/03-building-pages-2026-03-17-14-59-42.png) 3. In the popup, click **"Configure fields"** and check the fields you need (Title, Status, Priority, etc.); click **"Configure actions"** and enable the **"Submit"** button. ![03-building-pages-2026-03-17-15-00-54](https://static-docs.nocobase.com/03-building-pages-2026-03-17-15-00-54.png) ![03-building-pages-2026-03-17-15-01-49](https://static-docs.nocobase.com/03-building-pages-2026-03-17-15-01-49.png) 4. Fill in a few test tickets and submit — you'll see the data appear in the table. ![03-building-pages-2026-03-17-15-03-04](https://static-docs.nocobase.com/03-building-pages-2026-03-17-15-03-04.png) > We'll cover form configuration in detail (field linkage, edit forms, detail popups, etc.) in [Chapter 4](/tutorials/v2/04-forms-and-details/). For now, just being able to enter data is enough. ## 3.4 Configuring Display Columns By default, a table won't automatically show all [fields](/data-sources/field). We need to manually choose which columns to display: 1. On the right side of the Table block header, click **"Fields"**. 2. Check the fields you want to show: - **Title** — the ticket subject, visible at a glance - **Status** — current processing progress - **Priority** — urgency level - **Category** — a relation field that will display the category name - **Submitter** — who submitted the ticket - **Assignee** — who is responsible 3. Fields you don't need to display (like ID or Created at) can be left unchecked to keep the table clean. ![03-building-pages-2026-03-13-08-47-18](https://static-docs.nocobase.com/03-building-pages-2026-03-13-08-47-18.png) > **Tip**: You can drag and drop to reorder the displayed fields. Put the most important ones — "Title" and "Status" — up front so key information is visible at a glance. ### Relation Fields Showing IDs After enabling "Category," you'll notice the table shows category IDs (numbers) instead of names. That's because relation fields default to using ID as the title field. Two ways to fix this: **Option A: Change it in the column settings (current table only)** Click the column settings for the "Category" column and find **"Title field"**. Change it from ID to **Name**. This only affects the current Table block. ![03-building-pages-2026-03-13-09-23-03](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-23-03.png) ![03-building-pages-2026-03-13-09-24-44](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-24-44.gif) **Option B: Change it in the data source (global, recommended)** Go to **Settings -> [Data sources](/data-sources) -> Collections -> Categories**, and change the **"Title field"** to **Name**. All blocks referencing the Categories collection will then display names by default. After the change, you'll need to re-add the field on the page for it to take effect. ![03-building-pages-2026-03-13-09-23-41](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-23-41.png) ## 3.5 Adding Filtering and Sorting As tickets pile up, we'll need to quickly find specific ones. NocoBase provides several ways to filter data — let's start with the most common: the **Filter Form block**. ### Adding a Filter Form 1. On the All Tickets page, click **"Add block"** and select **Filter blocks -> Filter form**. 2. In v2 pages, there's no collection selection step — the Filter form is added directly to the page. 3. In the Filter form, click **"Fields"**. A list of all filterable data blocks on the current page will appear, e.g., `Table: Tickets #c48b` (the code after `#` is the block's UID, useful for distinguishing multiple blocks from the same collection). ![03-building-pages-2026-03-13-08-48-37](https://static-docs.nocobase.com/03-building-pages-2026-03-13-08-48-37.png) 4. Hover over a block name to expand its list of filterable fields. Click to add them: **Status**, **Priority**, **Category**. ![03-building-pages-2026-03-13-09-25-44](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-25-44.png) 5. Once added, users can type filter criteria and the table data will **update automatically in real time**. ![03-building-pages-2026-03-13-09-27-23](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-27-23.gif) ### Multi-Field Fuzzy Search What if you want a single search box to match across multiple fields at once? Click the settings icon in the top-right corner of a search field and you'll see **"Connect fields"**. It lists all searchable fields from each block on the page — by default, only "Title" is connected. ![03-building-pages-2026-03-13-09-30-06](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-30-06.png) Select additional fields like **Description** so that a keyword search matches all of them simultaneously. You can even search through relation fields — click "Category," then in the next level check "Category Name." Now searches will also match against category names. ![03-building-pages-2026-03-13-09-31-35](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-31-35.png) ![03-building-pages-2026-03-13-09-32-20](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-32-20.png) > **Connect fields is powerful**: it works across multiple blocks and multiple fields. If your page has several data blocks, try adding more and experiment! ### Don't Want Auto-Filtering? If you'd prefer users to click a button before filtering takes effect, click **"[Actions](/interface-builder/actions)"** at the bottom-right of the Filter form and enable the **"Filter"** and **"Reset"** buttons. Users will then need to click "Filter" to apply their criteria. ![03-building-pages-2026-03-13-09-33-15](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-33-15.png) ### Alternative: The Table's Built-in Filter Action Besides a dedicated Filter Form block, the Table block itself has a built-in **"Filter"** action. Click **"Actions"** above the Table block and enable **"Filter"**. A filter button will appear in the table toolbar. Clicking it opens a condition panel where users can filter data by field values directly. ![03-building-pages-2026-03-13-09-34-25](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-34-25.png) ![03-building-pages-2026-03-13-09-36-09](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-36-09.png) If you don't want users to hunt for fields every time they open the filter, you can pre-configure default filter fields in the Filter button's settings — so the most common criteria are ready to use right away. ![03-building-pages-2026-03-13-09-38-37](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-38-37.png) > **Note**: The table's built-in Filter action currently **does not support fuzzy search** — it only handles exact matches and condition-based filtering. If you need fuzzy search, use the Filter Form block with "Connect fields" described above. ### Setting Default Sorting We want the newest tickets to appear at the top: 1. Click the **block settings** icon (three-line icon) in the top-right corner of the Table block. 2. Find **"Set default sorting rules"**. 3. Add a sort field: select **Created at**, set the order to **Descending**. ![03-building-pages-2026-03-13-09-40-54](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-40-54.png) This way, newly submitted tickets always appear at the top, making them easier to handle. ## 3.6 Configuring Row Actions Viewing a list isn't enough — we also need to click into tickets to see details and make edits. 1. In the actions column, click the second "+" icon. 2. Click to add actions: **View**, **[Edit](/interface-builder/actions/edit)**, **[Delete](/interface-builder/actions/delete)**. 3. Each row will now have "View", "Edit", and "Delete" buttons in the actions column. ![03-building-pages-2026-03-13-09-42-42](https://static-docs.nocobase.com/03-building-pages-2026-03-13-09-42-42.png) Clicking the "View" or "Edit" button opens a Drawer where we can place blocks to show or edit the full record. We'll configure that in detail in the next chapter. Clicking "Delete" removes the row. ## 3.7 Adjusting Page Layout By now the page has both a Filter Form and a Table block, but they're stacked vertically by default — which may not look great. NocoBase lets you **drag and drop** to rearrange blocks. In design mode, hover over the drag handle at the top-left corner of a block (the cursor will change to a crosshair), then hold and drag. **Drag the Filter Form above the Table**: Grab the Filter Form block and move it toward the top edge of the Table block. When a blue guide line appears, release — the Filter Form will snap into place above the Table. **Drag filter fields onto the same row**: Inside the Filter Form, fields are stacked vertically by default. Drag "Priority" to the right of "Status" — when a vertical guide line appears, release. The two fields will sit side by side on one row, saving vertical space. > Almost everything in NocoBase supports drag-and-drop — action buttons, table columns, menu items, and more. Feel free to explore! ## 3.8 Configuring the Categories Page Don't forget — we created a "Categories" sub-page back in section 3.2. Now let's add content to it. The setup is similar to the ticket list — add a Table block, check fields, configure actions — so we won't repeat every step. Just one key difference. Remember the "Categories" collection we created in Chapter 2? It's a **tree table** (supports parent-child hierarchy). To display the tree structure correctly, you need to enable a setting: 1. Go to the "Categories" page and add a Table block, selecting the "Categories" collection. 2. Click the Table block's **block settings** (three-line icon), find **"Tree table"**, and toggle it on. Once enabled, the table will display categories in an indented hierarchy showing parent-child relationships, instead of listing all records flat. 3. Check the fields you want to display (e.g., Name, Description), and configure row actions ([Add new](/interface-builder/actions/add-new), Edit, Delete). 4. **Layout tip**: Put "Name" in the first column and "Actions" in the second. The categories table doesn't have many fields, so a two-column layout is more compact and user-friendly. [Screenshot: Categories tree table configured] ## Summary Congratulations! Our ticket system now has a proper **management interface**: - A clear menu structure (Tickets -> All Tickets / Categories) - A **Table block** displaying ticket data - A **Filter Form** for quick filtering by status, priority, and category - **Sorting rules** that order tickets by creation time, newest first - Row action buttons for convenient viewing and editing - A **tree table** displaying category hierarchy Easier than you expected, right? The entire process required zero lines of code — everything was done through drag-and-drop and configuration. ## Next Chapter Preview Being able to "see" data isn't enough — users also need to **submit new tickets**. In the next chapter, we'll build Form blocks, configure field linkage rules, and enable change history to track every modification to a ticket. ## Related Resources - [Blocks Overview](/interface-builder/blocks) — All block types explained - [Table Block](/interface-builder/blocks/data-blocks/table) — Table block configuration guide - [Filter Block](/interface-builder/blocks/filter-blocks/form) — Filter form setup --- url: /tutorials/v2/04-forms-and-details.md --- # Chapter 4: Forms & Details — Input, Display, All in One In the last chapter, we built the ticket list and used a quick form to enter test data. In this chapter, we'll **refine the form experience** — optimize [Form block](/interface-builder/blocks/data-blocks/form) field layouts, add [Details blocks](/interface-builder/blocks/data-blocks/details), configure [linkage rules](/interface-builder/linkage-rules), and use [change history](https://docs.nocobase.com/record-history/) to track every modification. :::tip Section 4.4 "Change History" requires the [Professional edition](https://www.nocobase.com/en/commercial). Skipping it won't affect later chapters. ::: ## 4.1 Refining the New Ticket Form In the last chapter, we quickly created a working "Add new" form. Now let's refine it — adjust field order, set default values, and optimize the layout. If you skipped the quick form in the previous chapter, no worries — we'll walk through creating one from scratch here. ### Adding the "Add new" Action Button 1. Make sure you're in [UI Editor](/get-started/how-nocobase-works) mode (top-right toggle is on). 2. Go to the "All Tickets" page, and click **"[Actions](/interface-builder/actions)"** above the Table block. 3. Check the **"Add new"** action button. 4. An "Add new" button will appear above the table. Clicking it opens a [popup](/interface-builder/actions/pop-up). ![04-forms-and-details-2026-03-13-09-43-54](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-09-43-54.png) ### Configuring the Form in the Popup 1. Click the "Add new" button to open the popup. 2. In the popup, click **"Add block" -> Data blocks -> [Form (Add new)](/interface-builder/blocks/data-blocks/form)**. 3. Select **"Current collection"**. The popup already knows which collection it's associated with — no need to specify manually. ![04-forms-and-details-2026-03-13-09-44-50](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-09-44-50.png) 4. In the form, click **"[Fields](/data-sources/field)"** and check the following fields: | Field | Configuration Notes | |-------|-------------------| | Title | Required (auto-inherited) | | Description | Rich text input | | Status | Dropdown select (we'll set a default via linkage rules later) | | Priority | Dropdown select | | Category | A relation field that automatically appears as a dropdown selector | | Submitter | Relation field (we'll set a default via linkage rules later) | | Assignee | Relation field | ![04-forms-and-details-2026-03-13-12-44-49](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-12-44-49.png) You'll notice a red asterisk `*` next to "Title" automatically — because we set it as required when creating the field in Chapter 2. Forms automatically inherit required rules from the [collection](/data-sources/main/collection)'s field settings; no extra configuration needed. ![04-forms-and-details-2026-03-13-12-46-34](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-12-46-34.png) > **Tip**: If a field isn't required at the collection level but you want it required in this specific form, you can set it individually in the field's settings. ![04-forms-and-details-2026-03-13-12-47-26](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-12-47-26.png) ### Adding the Submit Button 1. Below the Form block, click **"Actions"**. 2. Check the **"Submit"** button. ![04-forms-and-details-2026-03-13-16-30-06](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-16-30-06.png) 3. After filling in the form, users can click Submit to create a new ticket. ![04-forms-and-details-2026-03-13-16-32-19](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-16-32-19.gif) ## 4.2 Linkage Rules: Defaults & Field Linkage Some fields should be auto-filled (e.g., Status defaults to "Pending"), while others need to change dynamically based on conditions (e.g., urgent tickets require a description). The default value feature in 2.0 is still evolving, so in this tutorial we'll use **Linkage Rules** for both default values and field linkage. 1. Click the **block settings** icon (three-line icon) in the top-right corner of the Form block. 2. Find **"Linkage rules"** — clicking it opens a configuration panel in the sidebar. ![04-forms-and-details-2026-03-13-16-43-35](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-16-43-35.png) ### Setting Default Values Let's first set default values for "Status" and "Submitter": 1. Click **"Add linkage rule"**. 2. **Leave the condition empty** — an unconditional linkage rule executes immediately when the form loads. ![04-forms-and-details-2026-03-13-16-47-34](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-16-47-34.png) 3. Configure the Actions: - Status field -> **Set default value** -> Pending - Submitter field -> **Set default value** -> Current user > **Important notes on setting values**: Always select **"Current form"** as the data source first. For relation fields (like Category, Submitter, Assignee — any many-to-one field), you must select the object property itself, not its expanded sub-fields. > > When selecting variables (like "Current user"), first **single-click** to select it, then **double-click** to fill it into the selection bar. ![04-forms-and-details-2026-03-13-17-01-06](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-01-06.png) ![04-forms-and-details-2026-03-13-17-02-20](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-02-20.png) ![04-forms-and-details-2026-03-13-17-03-41](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-03-41.png) If you want a field to be non-editable by the submitter (e.g., Status), you can set its **"Display mode"** to **"Readonly"** in the field settings. ![04-forms-and-details-2026-03-13-17-22-15](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-22-15.png) > **Three display modes**: Editable, Readonly (editing disabled but field appearance preserved), and Easy-reading (displays as plain text only). ![04-forms-and-details-2026-03-13-12-54-53](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-12-54-53.png) ### Urgent Ticket Requires Description Next, add a conditional linkage rule: when a user selects Priority as "Urgent", the Description field becomes **required**, reminding the submitter to clearly describe the situation. 1. Click **"Add linkage rule"**. ![04-forms-and-details-2026-03-13-17-07-34](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-07-34.png) 2. Configure the rule: - **Condition**: Current form / Priority **equals** Urgent - **Actions**: Description field -> set to **Required** ![04-forms-and-details-2026-03-13-17-08-46](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-08-46.png) ![04-forms-and-details-2026-03-13-17-18-16](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-18-16.png) 3. Save the rule. Now test it: select Priority as "Urgent" and a red asterisk `*` will appear next to the Description field, indicating it's required. Select any other priority and it reverts to optional. ![04-forms-and-details-2026-03-13-17-20-18](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-20-18.gif) Finally, apply what we've learned and adjust the layout a bit. ![04-forms-and-details-2026-03-13-17-25-55](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-25-55.png) > **What else can linkage rules do?** Beyond setting defaults and controlling required status, they can also show/hide fields and dynamically assign values. For example: when Status is "Closed", hide the Assignee field. We'll explore more in later chapters as we encounter these scenarios. ## 4.3 Details Block In the last chapter, we added a "View" button to table rows that opens a Drawer. Now let's configure what goes inside the Drawer. 1. In the table, click the **"View"** button on any row to open the Drawer. 2. In the Drawer, click **"Add block" -> Data blocks -> [Details](/interface-builder/blocks/data-blocks/details)**. 3. Select **"Current collection"**. ![04-forms-and-details-2026-03-13-17-27-02](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-27-02.png) 4. In the Details block, click **"Fields"** with this layout: | Area | Fields | |------|--------| | Top | Title, Status (tag style) | | Main body | Description (rich text area) | | Side info | Category name, Priority, Submitter, Assignee, Created at | How to add a large title? Select Fields > Markdown > Edit Markdown > in the editing area, select a variable > Current record > Title. This dynamically inserts the record's title into a Markdown block. Delete the default text and use Markdown syntax to make it a heading (add `##` followed by a space before it). ![04-forms-and-details-2026-03-13-17-36-26](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-36-26.png) ![04-forms-and-details-2026-03-13-17-39-51](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-39-51.png) Now the original Title field can be removed. Adjust the details layout: ![04-forms-and-details-2026-03-13-17-43-36](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-43-36.png) > **Tip**: You can drag multiple fields onto the same row for a more compact and visually appealing layout. 5. In the Details block's **"Actions"**, check the **"Edit"** button so users can jump straight from the details view into edit mode. ![04-forms-and-details-2026-03-13-17-45-15](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-45-15.png) ### Configuring the Edit Form Click the "Edit" button and a new popup opens — it needs an edit form inside. The edit form has nearly the same fields as the "Add new" form. Do we really have to check all those fields again from scratch? Nope. Let's **save the "Add new" form as a template** first, then the edit form can reference it directly. **Step 1: Go back to the "Add new" form and save as template** 1. Close the current drawer, go back to the ticket list, and click "Add new" to open the form. 2. Click the **block settings** icon (three-line icon) at the top-right of the Form block, and find **"Save as template"**. ![04-forms-and-details-2026-03-13-17-47-21](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-47-21.png) 3. Click save — it defaults to **"Reference"**. All forms referencing this template share the same configuration. Update one place and all stay in sync. ![04-forms-and-details-2026-03-13-17-47-44](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-47-44.png) ![04-forms-and-details-2026-03-13-18-39-05](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-39-05.png) > Since our ticket form isn't complex, "Reference" is simpler to maintain. If you choose "Duplicate", each form gets an independent copy that can be modified separately. **Step 2: Reference the template in the edit popup** 1. Go back to the details drawer or the table's row actions, and click the "Edit" button to open the edit popup. You might think: just use **"Add block -> Other blocks -> Block templates"** to create the form, right? Try it and you'll find — this creates an **"Add new" form**, and the fields aren't actually populated. This is a common pitfall. ![04-forms-and-details-2026-03-13-17-59-36](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-17-59-36.png) The correct approach: 2. In the popup, click **"Add block" -> Data blocks -> Form (Edit)** to create an edit form block first. 3. In the edit form, click **"Fields" -> "Field templates"**, and select the template you saved earlier. 4. All fields will be populated at once, matching the "Add new" form exactly. 5. Don't forget to add a **"Submit"** action button so users can save their changes. ![04-forms-and-details-2026-03-13-18-05-13](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-05-13.png) ![04-forms-and-details-2026-03-13-18-15-11](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-15-11.gif) Want to add a field later? Just modify the template once — both the "Add new" and edit forms update automatically. ### Quick Editing: Change Data Without Opening a Popup Besides popup editing, NocoBase also supports **quick editing** directly in the table — no popups needed, just hover and click. There are two places to enable it: - **Table block level**: Click the Table block's **block settings** (three-line icon), find **"Quick editing"**, and toggle it on. This enables quick editing for all fields in the table. - **Individual field level**: Click a column's field settings, find **"Quick editing"**, and toggle it on per field. ![04-forms-and-details-2026-03-13-18-20-07](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-20-07.png) Once enabled, hovering over a table cell reveals a small pencil icon. Click it to open an inline editor for that field — changes save automatically. ![04-forms-and-details-2026-03-13-18-21-09](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-21-09.gif) > **When is this useful?** Quick editing is perfect for batch updates like changing status or assignee. For example, an admin browsing the ticket list can click the "Status" column to quickly change a ticket from "Pending" to "In Progress" without opening each one individually. ## 4.4 Enabling Change History :::info Commercial Plugin "[Record History](https://docs.nocobase.com/record-history/)" is included in the NocoBase [Professional edition](https://www.nocobase.com/en/commercial) and requires a commercial license. If you're using the Community edition, feel free to skip this section — it won't affect later chapters. ::: One of the most important aspects of a ticket system: **who changed what and when must be traceable**. NocoBase's "Record History" plugin automatically logs every data change. ### Configuring Change History 1. Go to **Settings -> Plugin manager** and make sure the "Record History" plugin is enabled. ![04-forms-and-details-2026-03-13-18-22-44](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-22-44.png) 2. Enter the plugin configuration page and click **"Add collection"**, then select **"Tickets"**. 3. Choose which fields to track: **Title, Status, Priority, Assignee, Description**, etc. ![04-forms-and-details-2026-03-13-18-25-11](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-25-11.png) > **Recommendation**: You don't need to track every field. Fields like ID and Created at that are never manually changed don't need to be tracked. Only record changes to fields that matter for the business. 4. Back in the settings, click **"Sync history data snapshot"**. The plugin will automatically create an initial history record for all existing tickets. Every subsequent change will add a new history entry. ![04-forms-and-details-2026-03-13-18-27-01](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-27-01.png) ![04-forms-and-details-2026-03-13-18-28-50](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-28-50.png) ### Viewing History in the Details Page 1. Go back to the ticket details Drawer (click the "View" button on a table row). 2. In the Drawer, click **"Add block" -> Record History**. 3. Select **"Current collection"** and choose **"Current record"** as the data source. 4. A timeline will appear at the bottom of the details page, clearly showing every change: who changed which field from what value to what value, and when. ![04-forms-and-details-2026-03-13-18-31-45](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-31-45.png) ![04-forms-and-details-2026-03-13-18-33-00](https://static-docs.nocobase.com/04-forms-and-details-2026-03-13-18-33-00.gif) This way, even if a ticket passes through multiple people, every change is crystal clear. ## Summary In this chapter, we completed the full data lifecycle: - **Form** — Users can submit new tickets with default values and validation - **Linkage rules** — Urgent tickets automatically require a description - **Details block** — Clearly displays a ticket's complete information - **[Change history](/collection-templates/audit-log)** — Automatically tracks every modification for worry-free auditing (commercial plugin, optional) From "visible" to "enterable" to "traceable" — our ticket system now has basic usability. ## Related Resources - [Form Block](/interface-builder/blocks/data-blocks/form) — Form block configuration guide - [Details Block](/interface-builder/blocks/data-blocks/details) — Details block setup - [Linkage Rules](/interface-builder/linkage-rules) — Field linkage configuration --- url: /tutorials/v2/05-roles-and-permissions.md --- # Chapter 5: Users & Permissions — Who Sees What In the last chapter, we built forms and detail [pages](/interface-builder/pages) — our ticket system can now handle data entry and viewing. But there's a problem: everyone sees the same thing after logging in. Regular employees who submit tickets can access the admin pages, technicians can delete categories... that's not going to work. In this chapter, we'll add "access control": create [roles](/users-permissions/role), configure [menu permissions](/users-permissions/role/menu-permissions) and [data scope](/users-permissions/role/data-scope) restrictions — **different people see different [menus](/interface-builder/menus) and operate on different data**. ## 5.1 Understanding Roles In NocoBase, **a [role](/users-permissions/role) is a collection of [permissions](/users-permissions/role)**. You don't need to configure permissions for each user individually — instead, you define a few roles first, then assign users to the appropriate roles. NocoBase comes with three built-in roles after installation: - **Root**: Super admin with full permissions — cannot be deleted - **Admin**: Administrator with UI configuration permissions by default - **Member**: Regular member with limited default permissions But these three built-in roles aren't enough for our needs. Our ticket system requires finer-grained control, so we'll create 3 custom roles next. ## 5.2 Creating Three Roles Open the settings menu in the top-right corner and go to **Users & Permissions -> Roles**. Click **Add role** and create the following roles one by one: | Role Name | Role Key | Description | |-----------|----------|-------------| | HelpDesk Admin | admin-helpdesk | Can see all tickets, manage categories, assign handlers | | Technician | technician | Can only see tickets assigned to them, can process and close | | Regular User | user | Can only submit tickets and view their own submissions | ![05-roles-and-permissions-2026-03-13-19-03-14](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-19-03-14.png) > The **Role Key** is a unique internal ID used by the system. It cannot be changed after creation, so we recommend using lowercase English. The Role Name can be modified at any time. ![05-roles-and-permissions-2026-03-13-18-57-47](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-18-57-47.png) After creation, you should see our three new roles in the roles list. ## 5.3 Configuring Menu Permissions Now that the roles are set up, we need to tell the system which menus each role can access. Click on a role to enter its permission configuration page, and find the **Menu permissions** tab. This lists all menu items in the system — check a box to allow access, uncheck it to hide it. **HelpDesk Admin (admin-helpdesk)**: Check all - Tickets, Categories, Dashboard — all visible **Technician (technician)**: Partial access - ✅ Tickets - ✅ Dashboard - ❌ Categories (technicians don't need to manage categories) **Regular User (user)**: Minimum permissions - ✅ My Tickets (or the "Submit Ticket" page) - ❌ Tickets - ❌ Categories - ❌ Dashboard ![05-roles-and-permissions-2026-03-13-19-09-11](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-19-09-11.png) > **Tip**: NocoBase has a handy setting — "Allow access to new menu items by default." If you don't want to manually check every new page you add, you can enable this for the admin role. For the regular user role, we recommend keeping it disabled. ## 5.4 Configuring Data Permissions Menu permissions control "can I access this page?" while data permissions control "what data can I see once I'm on the page?" Key concept: **[Data Scope](/users-permissions/role/data-scope)**. In the role's permission settings, switch to the **[Action permissions](/users-permissions/role/action-permissions)** tab. Find our "Tickets" [collection](/data-sources/main/collection) and click to configure it individually. ![05-roles-and-permissions-2026-03-13-19-51-06](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-19-51-06.png) ### Regular User: Only See Their Own Tickets 1. Find the **View** permission for the "Tickets" collection 2. Set the data scope to **Own records** 3. This way, regular users can only see tickets where they are the creator (note: the default "Own records" is based on the system creator field, not the Submitter field, but this can be changed) Similarly, set the "Edit" and "Delete" permissions to **Own records** as well (or simply don't grant delete permission at all). ![05-roles-and-permissions-2026-03-13-19-53-02](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-19-53-02.png) About global configuration: if you only configure the Tickets collection, other data and settings (like categories, assignees) may become invisible. Since our system is fairly simple, we'll check "View all data" globally for now, and only set specific data scopes for sensitive collections. ![05-roles-and-permissions-2026-03-13-19-57-24](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-19-57-24.png) ### Technician: Only See Tickets Assigned to Them 1. Find the **View** permission for the "Tickets" collection 2. Set the data scope to **Own records** 3. There's a nuance here — NocoBase's "Own records" filters by creator by default. If we want to filter by "Assignee" instead, we can further adjust this in the global operation permissions, or achieve it on the frontend by setting **filter conditions on the data [block](/interface-builder/blocks)** ![05-roles-and-permissions-2026-03-13-20-01-54](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-20-01-54.png) > **Practical tip**: You can also set default filter conditions on table blocks to assist permission control, e.g., "Assignee = Current user." But note that page configuration applies globally — admins would be limited too. A compromise: configure "Assignee = Current user **or** Submitter = Current user" to cover both regular users and technicians; if admins need a full view, create a separate page without filter conditions. ![05-roles-and-permissions-2026-03-13-22-21-34](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-22-21-34.png) ### HelpDesk Admin: See All Data For the admin role, set the data scope to **All records** and enable all operations. Simple and straightforward. ![05-roles-and-permissions-2026-03-13-21-45-14](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-21-45-14.png) ## 5.5 Ticket Assignment Action Before testing permissions, let's add a handy feature to the ticket list: **assigning a handler**. Admins can assign a ticket to a technician directly from the list, without opening the full edit form and dealing with a bunch of fields. The implementation is simple — add a custom popup button to the table row actions: 1. Enter [UI Editor](/get-started/how-nocobase-works) mode. In the ticket list table's actions column, click **"+"** to add a **"Popup"** action button. ![05-roles-and-permissions-2026-03-14-13-57-31](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-14-13-57-31.png) 2. Change the button title to **"Assign"** (click the button settings to modify the title). ![05-roles-and-permissions-2026-03-14-13-59-22](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-14-13-59-22.png) Since there's only a simple assignment to make, a compact dialog is more appropriate than a drawer. Click the popup settings in the button's top-right corner, select **Dialog (narrow)** > Confirm. ![05-roles-and-permissions-2026-03-14-14-08-16](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-14-14-08-16.png) 3. Click the "Assign" button to open the popup. In the popup, go to **"Add block → Data blocks → Form (Edit)"**, and select the current collection. 4. In the form, only check the **"Assignee"** field, and set it as **required** in the field settings. 5. Add a **"Submit"** action button. ![05-roles-and-permissions-2026-03-14-14-10-50](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-14-14-10-50.png) Now admins can click "Assign" in the ticket list, pick a handler from a minimal form, and submit. Quick, precise, and no risk of accidentally changing other fields. ### Controlling Button Visibility with Linkage Rules The "Assign" button is only needed by admins — showing it to regular users and technicians would be confusing. We can use **linkage rules** to show or hide the button based on the current user's role: 1. In UI Editor mode, click the "Assign" button's settings and find **"Linkage rules"**. 2. Add a rule with the condition: **Current user / Roles / Role name** is not equal to **HelpDesk Admin** (the name corresponding to the admin-helpdesk role). 3. Set the action when the condition is met: **Hide** the button. This way, only users with the admin role can see the "Assign" button — it's automatically hidden for all other roles. ![05-roles-and-permissions-2026-03-14-14-17-37](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-14-14-17-37.png) ## 5.6 Creating Test Users and Trying It Out Permissions are configured — let's verify them in practice. Go to **User Management** (in the settings center or the user management page you built earlier) and create 3 test users: | Username | Role | |----------|------| | Alice | HelpDesk Admin (admin-helpdesk) | | Bob | Technician (technician) | | Charlie | Regular User (user) | ![05-roles-and-permissions-2026-03-13-22-23-47](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-22-23-47.png) After creating them, log in with each account and check two things: **1. Are the menus displayed as expected?** - Alice → Can see all menus ![05-roles-and-permissions-2026-03-14-14-19-29](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-14-14-19-29.png) - Bob → Only sees Tickets and Dashboard ![05-roles-and-permissions-2026-03-13-22-26-50](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-22-26-50.png) - Charlie → Only sees "My Tickets" ![05-roles-and-permissions-2026-03-13-22-30-57](https://static-docs.nocobase.com/05-roles-and-permissions-2026-03-13-22-30-57.png) **2. Is the data filtered as expected?** - First, log in as Alice and create a few tickets assigned to different people - Switch to Bob → Only sees tickets assigned to them - Switch to Charlie → Only sees tickets they submitted Pretty cool, right? The same system, completely different content for different users! That's the power of permissions. ## Summary In this chapter, we completed the ticket system's permission framework: - **3 roles**: HelpDesk Admin, Technician, Regular User - **Menu permissions**: Control which pages each role can access - **Data permissions**: Control which data each role can see (via data scope) - **Test verification**: Log in with different accounts to confirm permissions work At this point, our ticket system is shaping up nicely — it can handle data entry, viewing, and role-based access control. But everything is still manual. ## Next Chapter Preview In the next chapter, we'll learn about **Workflows** — letting the system do work for us automatically. For example, automatically notifying the assignee when a ticket is submitted, or logging a timestamp when the status changes. ## Related Resources - [User Management](/users-permissions/user) — User administration guide - [Roles & Permissions](/users-permissions/role) — Role configuration reference - [Data Scope](/users-permissions/role/data-scope) — Row-level permission control --- url: /tutorials/v2/06-workflows.md --- # Chapter 6: Workflows — Let the System Do the Work In the last chapter, we added permissions so different roles see different content. But all operations are still done manually — when a new ticket comes in, someone has to go check; when a status changes, nobody gets notified. In this chapter, we'll use NocoBase's [Workflow](/workflow) engine to make the system **do things automatically** — configure [condition](/workflow/nodes/condition) checks and [update](/workflow/nodes/update) nodes for automatic ticket status transitions and timestamp recording. ## 6.1 What Is a Workflow? A workflow is a set of automated "if... then..." rules. Think of it like an alarm on your phone that goes off every morning at 8 AM. That alarm is the simplest workflow — **when a condition is met (it's 8 AM), an action executes automatically (the alarm rings)**. NocoBase workflows follow the same idea: ![06-workflows-2026-03-20-13-25-38](https://static-docs.nocobase.com/06-workflows-2026-03-20-13-25-38.jpg) - **[Trigger](/workflow/triggers/collection)**: The entry point for the workflow. For example, "someone created a new ticket" or "a record was updated" - **Condition**: An optional filtering step. For example, "only continue if the assignee is not empty" - **Action**: The step that does the actual work. For example, "send a notification" or "update a [field](/data-sources/field)" Actions can be chained with multiple nodes. Common node types include: - **Flow control**: Condition, Parallel [branch](/workflow/nodes/condition), Loop, Delay - **Data operations**: [Create record](/workflow/nodes/create), Update record, Query record, Delete record - **Notifications & external**: Notification, HTTP request, Calculation This tutorial only covers a few of the most common ones — once you learn to combine them, you'll be able to handle most scenarios. ### Trigger Types at a Glance NocoBase offers several trigger types, which you select when creating a workflow: | Trigger | Description | Typical Use Case | |---------|-------------|-----------------| | **[Collection event](/workflow/triggers/collection)** | Fires when a record is created, updated, or deleted | New ticket notification, status change logging | | **[Schedule](/workflow/triggers/schedule)** | Fires on a Cron expression or fixed time | Daily reports, periodic data cleanup | | **[Post-action event](/workflow/triggers/action)** | Fires after a user performs a UI action | Send notification after form submission | | **Approval** | Initiates an approval flow with multi-level support | Leave requests, purchase approvals | | **Custom action** | Bound to a custom button, fires on click | One-click archiving, batch operations | | **Pre-action event** | Intercepts a user action synchronously before it completes | Pre-submit validation, auto-fill fields | | **AI Employee** | Exposes the workflow as a tool for AI employees to call | AI-driven business operations | This tutorial uses both **Collection event** and **Custom action event** triggers. The other types work similarly; once you've learned these, you can pick up the rest quickly. NocoBase workflows are a built-in plugin — no extra installation needed, ready to use out of the box. ## 6.2 Scenario 1: Automatically Notify the Assignee on New Tickets **Requirement**: When someone creates a new ticket and specifies an assignee, the system automatically sends an in-app message to the assignee, letting them know "there's work to do." ### Step 1: Create a Workflow Open the plugin settings menu in the top-right corner and go to **Workflow management**. ![06-workflows-2026-03-14-23-50-45](https://static-docs.nocobase.com/06-workflows-2026-03-14-23-50-45.png) Click **New**, and in the dialog that appears: - **Name**: Enter "Notify assignee on new ticket" - **Trigger type**: Select **Collection event** ![06-workflows-2026-03-14-23-53-37](https://static-docs.nocobase.com/06-workflows-2026-03-14-23-53-37.png) After submitting, click the **Configure** link in the list to enter the flow editor. ### Step 2: Configure the Trigger Click the trigger card at the top to open its configuration drawer: - **[Collection](/data-sources/main/collection)**: Select Main datasource / "Tickets" - **Trigger on**: Select "After record created or updated" - **Changed fields**: Check "Assignee" — the workflow only triggers when the Assignee field changes, avoiding unnecessary notifications from other field updates (when creating a record, all fields are considered changed, so new tickets will also trigger) - **Only triggers when conditions are met**: Set the mode to "Match **any** condition in the group," then add two conditions: - `assignee_id` is not empty - `Assignee / ID` is not empty > Why two conditions? Because at trigger time, the form may only have the foreign key (assignee_id) without the associated object loaded, or vice versa. Using OR ensures the workflow triggers as long as an assignee is specified. - **Preload associations**: Check "Assignee" — the notification node needs the assignee's information, so it must be preloaded in the trigger ![06-workflows-2026-03-14-23-58-31](https://static-docs.nocobase.com/06-workflows-2026-03-14-23-58-31.png) Click Save. The trigger itself now handles the condition filtering — it only fires when the Assignee is not empty, so there's no need for a separate condition node. ### Step 3: Add a Notification Node Click the **+** below the trigger and select a **Notification** node. ![06-workflows-2026-03-15-00-00-55](https://static-docs.nocobase.com/06-workflows-2026-03-15-00-00-55.png) Open the notification node's configuration. The first option is to select a **Notification channel** — but we haven't created one yet, so the dropdown is empty. Let's go create one first. ![06-workflows-2026-03-15-00-10-12](https://static-docs.nocobase.com/06-workflows-2026-03-15-00-10-12.png) ### Step 4: Create a Notification Channel NocoBase supports multiple notification channel types: | Channel Type | Description | |-------------|-------------| | **In-app message** | Browser notifications, pushed in real-time to the user's notification center | | **Email** | Sends via SMTP, requires email server configuration | For this tutorial, we'll use the simplest option — **In-app message**: 1. Open the plugin settings in the top-right corner and go to **Notification management** 2. Click **Create channel** ![06-workflows-2026-03-15-00-13-07](https://static-docs.nocobase.com/06-workflows-2026-03-15-00-13-07.png) 3. Select **In-app message** as the channel type, and enter a name (e.g., "System In-App Messages") 4. Save ![06-workflows-2026-03-15-00-17-55](https://static-docs.nocobase.com/06-workflows-2026-03-15-00-17-55.png) ### Step 5: Configure the Notification Node Go back to the workflow editor and open the notification node's configuration. The notification node has the following configuration options: - **Notification channel**: Select the "System In-App Messages" channel you just created - **Receivers**: Click to select Query users → set `id = Trigger variable / Trigger data / Assignee / ID` - **Title**: Enter a notification title, e.g., "You have a new ticket to handle." Supports variables — for example, include the ticket title: `New ticket: {{Trigger data / Title}}` - **Content**: Enter the notification body. You can also insert variables to reference the ticket's priority, description, and other fields ![06-workflows-2026-03-15-20-10-11](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-10-11.png) (Before leaving the popup for the next step, remember to save first!) - **Desktop detail page**: Enter the URL path of the ticket detail page. To get it: open any ticket's detail popup in the frontend, then copy the path from the browser address bar — it looks like `/admin/camcwbox2uc/view/d8f8e122d37/filterbytk/353072988225540`. Paste the path into the field, then replace the number after `filterbytk/` with the trigger data's ID variable (click the variable selector → Trigger data → ID). Once configured, clicking the notification in the list will navigate directly to that ticket's detail page and automatically mark it as read ![06-workflows-2026-03-15-00-28-32](https://static-docs.nocobase.com/06-workflows-2026-03-15-00-28-32.png) ![06-workflows-2026-03-15-20-15-19](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-15-19.png) - **Continue on failure**: Optional. If checked, the workflow won't stop even if the notification fails to send > After the notification is sent, the assignee can see the message in the **Notification center** (top-right corner of the page). Unread items will show a red dot indicator. Clicking the notification takes you directly to the ticket detail page. ### Step 6: Test and Enable > The complete flow for Scenario 1 has just two nodes: Trigger (with condition filtering) → Notification. Simple and direct. Don't rush to enable it — workflows provide a **manual execution** feature that lets you test the flow with specific data first: 1. Click the **Execute** button in the top-right corner (not the enable toggle) 2. Select an existing ticket record as the trigger data > If the ticket selector shows IDs instead of titles, go to Data sources → Collections → Tickets and set the "Title" column as the title field. ![06-workflows-2026-03-15-19-47-57](https://static-docs.nocobase.com/06-workflows-2026-03-15-19-47-57.png) 3. Click execute. The workflow will run and automatically switch to a duplicated new version. ![06-workflows-2026-03-15-19-57-19](https://static-docs.nocobase.com/06-workflows-2026-03-15-19-57-19.png) 4. Click the three dots in the top-right corner and select Execution history. You should see the execution record we just created. Click to view the details — including trigger info, each node's execution details, and parameters. ![06-workflows-2026-03-15-19-58-34](https://static-docs.nocobase.com/06-workflows-2026-03-15-19-58-34.png) ![06-workflows-2026-03-15-20-01-02](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-01-02.png) 5. The ticket we just tested seems to be assigned to Alice. Let's switch to Alice's account — notification received! ![06-workflows-2026-03-15-20-16-22](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-16-22.png) Click the notification to jump to the target ticket page. The notification is automatically marked as read. ![06-workflows-2026-03-15-20-16-54](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-16-54.png) Once confirmed, click the **On/Off** toggle in the top-right corner to enable the workflow. ![06-workflows-2026-03-15-20-18-16](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-18-16.png) > **Note**: Once a workflow has been executed (including manual execution), it becomes **read-only** (grayed out) and can no longer be edited. If you need to make changes, click **"Duplicate to new version"** in the top-right corner and continue editing the new version. The old version is automatically disabled. ![06-workflows-2026-03-15-20-19-11](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-19-11.png) Go back to the tickets page and create a new ticket — make sure to select an assignee. Then switch to the assignee's account and check the notification center — you should see a new notification. ![06-workflows-2026-03-15-20-22-00](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-22-00.gif) Congratulations, your first automation is up and running! ## 6.3 Scenario 2: Automatically Record Completion Time **Requirement**: When a ticket is marked as "Completed," the system automatically fills in the "Completion time" field with the current time. No manual entry needed, and it never gets forgotten. > If you haven't created a "Completion time" field in the Tickets collection yet, go to **Collection management → Tickets** and add a **Date** type field named "Completion time." Refer to Chapter 2 for how to create fields — we won't repeat the steps here. > ![06-workflows-2026-03-15-20-25-38](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-25-38.png) ### Step 1: Create a Workflow Go back to the workflow management page and click New: - **Name**: Enter "Auto-record ticket completion time" - **Trigger type**: Select **Custom action event** (fires when a user clicks a button bound to this workflow) - **Execution mode**: Synchronous > About synchronous vs asynchronous: > - Asynchronous: After the action, you can continue doing other things while the workflow runs in the background > - Synchronous: After the action, the UI waits for the workflow to finish before you can do anything else ![06-workflows-2026-03-19-22-56-34](https://static-docs.nocobase.com/06-workflows-2026-03-19-22-56-34.png) ### Step 2: Configure the Trigger Open the trigger configuration: - **Collection**: Select "Tickets" - **Execution mode**: Select **Single record mode** (processes only the current ticket each time) ![06-workflows-2026-03-19-22-58-21](https://static-docs.nocobase.com/06-workflows-2026-03-19-22-58-21.png) ### Step 3: Add a Condition Unlike the collection event trigger which has built-in conditions, we need to add a condition node ourselves: ![06-workflows-2026-03-15-20-39-14](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-39-14.png) We recommend selecting "Continue on 'Yes' and 'No' separately" for easier future expansion. - Condition: **Trigger data → Status** does not equal **Completed** (only incomplete tickets pass through; already completed tickets won't be processed again) ![06-workflows-2026-03-19-22-37-59](https://static-docs.nocobase.com/06-workflows-2026-03-19-22-37-59.png) ### Step 4: Add an Update Record Node On the "Yes" branch of the condition, click **+** and select an **Update record** node: ![06-workflows-2026-03-15-20-46-22](https://static-docs.nocobase.com/06-workflows-2026-03-15-20-46-22.png) - **Collection**: Select "Tickets" - **Filter condition**: ID equals Trigger data → ID (to ensure only the current ticket gets updated) - **Field values**: - Status = **Completed** - Completion time = **System variables / System time** ![06-workflows-2026-03-19-22-39-27](https://static-docs.nocobase.com/06-workflows-2026-03-19-22-39-27.png) > This way, a single node handles both "change status" and "record time" — no need to configure field values on the button separately. ### Step 5: Create a "Complete" Action Button The workflow is configured, but a "Custom action event" needs to be bound to a specific action button to trigger. Let's create a dedicated "Complete" button in the ticket list's action column: 1. Enter UI Editor mode. In the ticket table's action column, click **"+"** and select a **"Trigger workflow"** action button ![06-workflows-2026-03-19-22-41-31](https://static-docs.nocobase.com/06-workflows-2026-03-19-22-41-31.png) 2. Click the button settings and change the title to **"Complete"**, then select a completion-related icon (e.g., a checkmark icon) ![06-workflows-2026-03-19-22-43-39](https://static-docs.nocobase.com/06-workflows-2026-03-19-22-43-39.png) 3. Configure **linkage rules** for the button: hide it when the ticket status is already "Completed" (completed tickets don't need a "Complete" button) - Condition: Current data → Status equals Completed - Action: Hide ![06-workflows-2026-03-15-21-15-29](https://static-docs.nocobase.com/06-workflows-2026-03-15-21-15-29.png) 4. Open **"Bind workflows"** in the button settings and select the "Auto-record ticket completion time" workflow we just created ![06-workflows-2026-03-19-23-00-53](https://static-docs.nocobase.com/06-workflows-2026-03-19-23-00-53.png) ### Step 6: Configure Event Flow Refresh The button is created, but the table won't automatically refresh after clicking — users won't see the status change. We need to configure the button's **event flow** to automatically refresh the table after the workflow completes. 1. Click the second lightning bolt icon (⚡) in the button settings to open the **Event flow** configuration 2. Configure the trigger event: - **Trigger event**: Select **Click** - **Execution timing**: Select **After all flows** 3. Click **"Append step"** and select **"Refresh target block"** ![06-workflows-2026-03-20-16-46-59](https://static-docs.nocobase.com/06-workflows-2026-03-20-16-46-59.png) 4. Find the ticket table on the current page, open its settings menu, and click **"Copy UID"** at the bottom. Paste the UID into the refresh step's target block field ![06-workflows-2026-03-20-16-48-39](https://static-docs.nocobase.com/06-workflows-2026-03-20-16-48-39.png) This way, after clicking the "Complete" button, the table automatically refreshes once the workflow finishes, and users immediately see the status and completion time changes. ### Step 7: Enable and Test Go back to the workflow management page and enable the "Auto-record ticket completion time" workflow. Then open a ticket with "In Progress" status and click the **"Complete"** button in the action column. You should see: - The ticket's "Completion time" field is automatically filled with the current time - The table refreshes automatically, and the "Complete" button has disappeared for this ticket (linkage rule in effect) ![06-workflows-2026-03-15-21-25-11](https://static-docs.nocobase.com/06-workflows-2026-03-15-21-25-11.gif) Convenient, right? This is the second common use case for workflows — **automatically updating data**. And by using the "Custom action event + Button binding" approach, we've created a precise trigger mechanism: the workflow only runs when a specific button is clicked. ## 6.4 Viewing Execution History How many times has the workflow run? Were there any errors? NocoBase keeps track of everything. In the workflow management list, each workflow shows an **Execution count** link. Click it to see the detailed record of each execution: - **Execution status**: Success (green) or failure (red) — easy to spot at a glance - **Trigger time**: When was it triggered - **Node details**: Click in to see the execution result of each node ![06-workflows-2026-03-15-21-25-38](https://static-docs.nocobase.com/06-workflows-2026-03-15-21-25-38.png) If an execution failed, clicking into the details shows which node had the problem and the specific error message. This is the most important tool for debugging workflows. ![06-workflows-2026-03-15-21-36-16](https://static-docs.nocobase.com/06-workflows-2026-03-15-21-36-16.png) ## Summary In this chapter, we created two simple but practical workflows: - **New ticket notification** (Collection event trigger): Automatically notifies the assignee when a ticket is created or reassigned — no more yelling across the office - **Auto-record completion time** (Custom action event trigger): Click "Complete" and the timestamp is filled automatically — no more human oversight These two workflows demonstrate two different trigger types, and together took less than 10 minutes to configure. The system can already do things automatically. NocoBase supports many more node types (HTTP requests, calculations, loops, etc.), but for getting started, mastering these combos is enough to handle most scenarios. ## Next Chapter Preview The system can do things automatically now, but we're still missing a "big picture view" — how many tickets are there in total? Which category has the most? How many new tickets per day? In the next chapter, we'll use chart [blocks](/interface-builder/blocks) to build a **data dashboard** to see everything at a glance. ## Related Resources - [Workflow Overview](/workflow) — Core workflow concepts and use cases - [Collection Event Trigger](/workflow/triggers/collection) — Data change trigger configuration - [Update Record Node](/workflow/nodes/update) — Automatic data update setup --- url: /tutorials/v2/07-dashboard.md --- # Chapter 7: Dashboard — The Big Picture at a Glance In the last chapter, we used workflows to make the system send notifications and record timestamps automatically. The system is getting smarter, but we're still missing one thing — **a bird's-eye view**. How many tickets are there? How many have been resolved? Which category has the most issues? How many new tickets come in each day? You can't answer these questions by scrolling through a list. In this chapter, we'll use [chart blocks](/data-visualization) (pie, line, bar charts) and [Markdown blocks](/interface-builder/blocks/other/markdown) to build a **data dashboard** that turns raw data into something you can understand at a glance. ## 7.1 Adding a Dashboard Page First, let's add a new [menu](/interface-builder/menus) item to the top navigation bar. Enter [configuration mode](/get-started/how-nocobase-works), click **"Add menu item"** (`+` icon) on the top menu bar, select **"Modern page (v2)"**, and name it "Dashboard." ![07-dashboard-2026-03-15-21-39-35](https://static-docs.nocobase.com/07-dashboard-2026-03-15-21-39-35.png) This [page](/interface-builder/pages) is dedicated to charts — it's our dashboard home base. ## 7.2 Pie Chart: Ticket Status Distribution For our first chart, we'll use a pie chart to show how many tickets are "Pending," "In Progress," and "Completed." On the Dashboard page, click **Add block → [Chart](/data-visualization)**. After adding it, click the **Configure** button in the top-right corner of the [block](/interface-builder/blocks). A chart configuration panel will open on the right side. ### Configuring the Data Query - **[Collection](/data-sources/main/collection)**: Select "Tickets" - **Measures**: Select any unique [field](/data-sources/field) (e.g., ID), set the aggregation to **Count** - **Dimensions**: Select the "Status" field ![07-dashboard-2026-03-15-21-44-32](https://static-docs.nocobase.com/07-dashboard-2026-03-15-21-44-32.png) Click **Run query** to preview the returned data below. ### Configuring Chart Options - **Chart type**: Select **Pie** - **Field mapping**: Set Category to "Status" and Value to the count value - **Labels**: Turn on the toggle A nice-looking pie chart should now appear on the page. Each slice represents a status, showing the exact count and percentage by default. ![07-dashboard-2026-03-15-21-45-40](https://static-docs.nocobase.com/07-dashboard-2026-03-15-21-45-40.png) Click **Save** — your first chart is done. ## 7.3 Line Chart: Daily New Ticket Trend The pie chart shows "how things are distributed right now." A line chart shows "how things change over time." Add another chart block to the page with the following configuration: ### Data Query - **Collection**: Select "Tickets" - **Measures**: ID, Count - **Dimensions**: Select the "Created at" field, set the format to **YYYY-MM-DD** (group by day) > **Tip**: The date dimension format matters. Choosing `YYYY-MM-DD` groups by day; choosing `YYYY-MM` groups by month. Pick the right granularity based on your data volume. ### Chart Options - **Chart type**: Select **Line** - **Field mapping**: Set xField to "Created at" and yField to the count value ![07-dashboard-2026-03-15-21-48-33](https://static-docs.nocobase.com/07-dashboard-2026-03-15-21-48-33.png) After saving, you'll see how ticket volume changes over time. If there's a sudden spike on a particular day, something happened worth looking into. ## 7.4 Bar Chart: Tickets by Category For our third chart, let's see which category has the most tickets. We'll use a **horizontal bar chart** instead of a vertical column chart — when there are many categories, vertical X-axis labels tend to overlap and get hidden, so horizontal display is much clearer. Add a third chart block: ### Data Query - **Collection**: Select "Tickets" - **Measures**: ID Count - **Dimensions**: Select the "Category" relation field (choose the category's Name field) ### Chart Options - **Chart type**: Select **Bar** - **Field mapping**: Set xField to the count value (ID Count) and yField to "Category Name" ![07-dashboard-2026-03-15-22-05-11](https://static-docs.nocobase.com/07-dashboard-2026-03-15-22-05-11.png) After saving, it's immediately clear which category has the most issues. If the "Network Failure" bar stretches far beyond the rest, maybe it's time to upgrade the network equipment. ## 7.5 Table Block: Unresolved Tickets Charts give a summary view, but admins usually need to see specific details too. Let's add an **Unresolved Tickets** table that shows all tickets that haven't been completed yet. Add a **Table block** to the page, selecting the "Tickets" collection. ### Configure Filter Conditions Click the table block's settings and find **Set data scope**. Add a [filter](/interface-builder/blocks/filter-blocks/form) condition: - **Status** is not equal to **Completed** This way the table only shows unfinished tickets — once a ticket is completed, it automatically disappears from the list. ### Configure Fields Select the columns to display: Title, Status, Priority, Assignee, Created at. ![07-dashboard-2026-03-15-22-20-11](https://static-docs.nocobase.com/07-dashboard-2026-03-15-22-20-11.png) > **Tip**: You can also add a **default sort** (by Created at, descending) so the newest tickets appear at the top. ## 7.6 Markdown Block: System Announcements Beyond charts, we can also put some text information on the dashboard. Add a **[Markdown block](/interface-builder/blocks/other/markdown)** and write a system announcement or usage instructions: ```markdown ## IT HelpDesk System Welcome! If you run into any issues, please submit a ticket and the tech team will handle it ASAP. **For urgent issues**, call the IT hotline directly: 8888 ``` ![07-dashboard-2026-03-15-21-53-54](https://static-docs.nocobase.com/07-dashboard-2026-03-15-21-53-54.png) Place the Markdown block at the top of the dashboard — it works as both a welcome message and a bulletin board. The content can be updated anytime, making it very flexible. ## 7.7 JS Block: Personalized Welcome Banner Markdown has a fairly fixed format — what if you want richer effects? NocoBase provides a **JS Block (JavaScript Block)** that lets you freely customize display content with code. We'll use it to create a business-style welcome banner that shows a personalized greeting based on the current user and time of day. Add a **JS block** to the page (Add block → Other blocks → JS block). ![07-dashboard-2026-03-15-22-33-24](https://static-docs.nocobase.com/07-dashboard-2026-03-15-22-33-24.png) In the JS block, you can use `ctx.getVar("ctx.user.username")` to get the current user's username. Here's a clean, business-style welcome banner: ```js const uname = await ctx.getVar("ctx.user.username") const name = uname || 'User'; const hour = new Date().getHours(); const hi = hour < 12 ? 'Good morning' : hour < 18 ? 'Good afternoon' : 'Good evening'; const d = new Date(); const date = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; const weekDay = d.toLocaleDateString('en-US', { weekday: 'long' }); ctx.render(`
${hi}, ${name}
Welcome back to IT HelpDesk
${date} ${weekDay}
`); ``` ![07-dashboard-2026-03-15-22-51-27](https://static-docs.nocobase.com/07-dashboard-2026-03-15-22-51-27.png) The result is a light gray card with the greeting on the left and date on the right. Clean, practical, and unobtrusive. > **Tip**: `ctx.getVar("ctx.user.xxx")` is how you access current user info in JS blocks. Common fields include `nickname`, `username`, and `email`. JS blocks can also call APIs to query data — you can use them for much more custom content later. ## 7.8 Action Panel: Quick Links + Popup Reuse A dashboard isn't just for viewing data — it should also be a starting point for actions. Let's add an **Action Panel** so users can submit tickets and jump to the ticket list directly from the homepage. Add an **Action Panel** block (Add block → Other blocks → Action Panel), then add two [actions](/interface-builder/actions) inside it: ![07-dashboard-2026-03-15-22-54-06](https://static-docs.nocobase.com/07-dashboard-2026-03-15-22-54-06.png) 1. **Link: Go to ticket list** — Add a "Link" action and configure the URL to point to the ticket list page (e.g., `/admin/camcwbox2uc`) ![07-dashboard-2026-03-15-22-57-49](https://static-docs.nocobase.com/07-dashboard-2026-03-15-22-57-49.png) 2. **Button: Add Ticket** — Add a "Popup" action button and change the title to "Add Ticket" ![07-dashboard-2026-03-15-23-00-36](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-00-36.png) But clicking "Add Ticket" opens an empty popup — we need to configure its content. Rebuilding the entire new ticket form from scratch would be tedious. This is where a very handy feature comes in: **Popup Reuse**. ### Saving a Popup Template > Note: Popup templates are different from the "block templates" we covered in Chapter 4. Block templates save a single form block's fields and layout, while popup templates save the **entire popup** — including all blocks, fields, and action buttons inside it. 1. Go to the **Tickets list page** and find the "Add Ticket" button 2. Click the button's settings and find **"Save as template"** — save the current popup 3. Give the template a name (e.g., "New Ticket Popup") ![07-dashboard-2026-03-15-23-05-17](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-05-17.png) ### Reusing the Popup on the Dashboard 1. Go back to the Dashboard page and click the "Add Ticket" button's settings in the action panel 2. Find **"Popup settings"** and select the "New Ticket Popup" template you just saved 3. After saving, clicking the button will open the exact same new ticket form popup as on the ticket list page ![07-dashboard-2026-03-15-23-06-33](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-06-33.png) ![07-dashboard-2026-03-15-23-07-20](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-07-20.gif) ### Click Title to Open Detail Popup Using the same approach, we can make ticket titles in the unresolved tickets table clickable to open the detail view directly: 1. First, go to the **Tickets list page**, find the "View" button's settings, and similarly **"Save as template"** (e.g., "Ticket Detail Popup") ![07-dashboard-2026-03-15-23-08-02](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-08-02.png) 2. Go back to the Dashboard page. In the unresolved tickets table, click the "Title" field's settings 3. Turn on the **"Enable click to open"** toggle — a "Popup settings" option will appear 4. In the popup settings, select the "Ticket Detail Popup" template you just saved ![07-dashboard-2026-03-15-23-11-24](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-11-24.png) Now users can click a ticket title on the dashboard to view its details instantly, without navigating to the ticket list page. The whole dashboard becomes more compact and efficient. ![07-dashboard-2026-03-15-23-12-36](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-12-36.png) > **Benefits of popup reuse**: The same popup template can be used across multiple pages. When you modify the template, all references update automatically. This is similar to the "Reference" mode from Chapter 4 — maintain in one place, apply everywhere. ## 7.9 Adjusting the Layout We now have 6 blocks on the page (JS welcome banner + Action panel + 3 charts + tickets table). Let's adjust the layout to make it look better. In configuration mode, you can **drag and drop** to reposition and resize each block. Suggested layout: - **Row 1**: JS welcome banner (left) + Action panel (right) - **Row 2**: Pie chart (left) + Tickets table (right) - **Row 3**: Line chart (left) + Bar chart (right) ![07-dashboard-2026-03-15-23-14-19](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-14-19.png) Note: you may find that block heights don't align. You can manually adjust this in Block settings → Block height — for example, set both blocks in row 2 to 500px. Drag the edges to adjust block widths so the two charts each take up half the row. Try a few arrangements until you find what looks best. ![07-dashboard-2026-03-15-23-18-57](https://static-docs.nocobase.com/07-dashboard-2026-03-15-23-18-57.png) ## Summary In this chapter, we built a rich and practical data dashboard with 6 blocks: - **JS welcome banner**: Personalized greeting based on current user and time - **Action panel**: Quick link to ticket list + one-click ticket creation (popup reuse) - **Pie chart**: See the ticket status distribution at a glance - **Line chart**: Track how ticket volume changes over time - **Bar chart**: Compare ticket counts across categories horizontally — no label overlap even with many categories - **Unresolved tickets table**: All pending tickets at a glance, click title to view details (popup reuse) We also learned an important technique — **Popup Reuse**: save a popup from one page as a template and reference it on other pages, avoiding repetitive configuration. Data visualization is a built-in NocoBase plugin — no additional installation needed. Configuring it is just as simple as building a page: pick your data, choose a chart type, map the fields — three steps and you're done. ## What's Next At this point, our ticket system is quite feature-complete: data modeling, page building, form entry, access control, automated workflows, and a data dashboard — we've got it all. We're planning an **AI Agent version of this tutorial** — using AI Agents to build the system locally and automatically. Stay tuned. ## Related Resources - [Data Visualization](/data-visualization) — Chart configuration guide - [Markdown Block](/interface-builder/blocks/other/markdown) — Markdown block usage - [Block Layout](/interface-builder/blocks) — Page layout and block configuration --- url: /data-visualization/guide/data-query.md --- # Data query The chart configuration panel is divided into three sections: Data query, Chart options, and Interaction events, plus Cancel, Preview, and Save buttons at the bottom. Let's first look at the "Data query" panel to understand the two query modes (Builder/SQL) and their common features. ## Panel Structure ![clipboard-image-1761466636](https://static-docs.nocobase.com/clipboard-image-1761466636.png) > Tips: To configure the current content more easily, you can collapse other panels first. The top is the action bar: - Mode: Builder (graphical, simple, and convenient) / SQL (handwritten statements, more flexible). - Run query: Click to execute the data query request. - View result: Opens the data result panel, where you can switch between Table/JSON views. Click again to collapse the panel. From top to bottom: - Data source and collection: Required. Select the data source and collection. - Measures: Required. The numeric fields to be displayed. - Dimensions: Group by fields (e.g., date, category, region). - Filter: Set filter conditions (e.g., =, ≠, >, <, contains, range). Multiple conditions can be combined. - Sort: Select the field to sort by and the order (ascending/descending). - Pagination: Control the data range and return order. ## Builder mode ### Select data source and collection - In the "Data query" panel, set the mode to "Builder". - Select a data source and collection. If the collection is not selectable or is empty, first check permissions and whether it has been created. ### Configure Measures - Select one or more numeric fields and set an aggregation: `Sum`, `Count`, `Avg`, `Max`, `Min`. - Common use cases: `Count` to count records, `Sum` to calculate a total. ### Configure Dimensions - Select one or more fields as grouping dimensions. - Date and time fields can be formatted (e.g., `YYYY-MM`, `YYYY-MM-DD`) to facilitate grouping by month or day. ### Filter, Sort, and Pagination - Filter: Add conditions (e.g., =, ≠, contains, range). Multiple conditions can be combined. - Sort: Select a field and the sort order (ascending/descending). - Pagination: Set `Limit` and `Offset` to control the number of returned rows. It's recommended to set a small `Limit` when debugging. ### Run Query and View Result - Click "Run query" to execute. After it returns, switch between `Table / JSON` in "View result" to check the columns and values. - Before mapping chart fields, confirm the column names and types here to avoid an empty chart or errors later. ![20251026174338](https://static-docs.nocobase.com/20251026174338.png) ### Subsequent Field Mapping Later, when configuring "Chart options", you will map fields based on the fields from the selected data source and collection. ## SQL mode ### Write Query - Switch to "SQL" mode, enter your query statement, and click "Run query". - Example (total order amount by date): ```sql SELECT TO_CHAR(order_date, 'YYYY-MM') as mon, SUM(total_amount) AS total FROM "order" GROUP BY mon ORDER BY mon ASC LIMIT 100; ``` ![20251026175952](https://static-docs.nocobase.com/20251026175952.png) ### Run Query and View Result - Click "Run query" to execute. After it returns, switch between `Table / JSON` in "View result" to check the columns and values. - Before mapping chart fields, confirm the column names and types here to avoid an empty chart or errors later. ### Subsequent Field Mapping Later, when configuring "Chart options", you will map fields based on the columns from the query result. > [!TIP] > For more information on SQL mode, please see Advanced Usage — Query Data in SQL Mode. --- url: /data-visualization/guide/chart-options.md --- # Chart options Configure how charts are displayed. Two modes are supported: Basic (visual) and Custom (JS). Basic is ideal for quick mapping and common properties; Custom fits complex scenarios and advanced customization. ## Panel layout ![clipboard-image-1761473695](https://static-docs.nocobase.com/clipboard-image-1761473695.png) > Tips: To configure more easily, collapse other panels first. Top action bar Mode selection: - Basic: Visual configuration. Choose a type and complete field mapping; adjust common properties with switches. - Custom: Write JS in the editor and return an ECharts `option`. ## Basic mode ![20251026190615](https://static-docs.nocobase.com/20251026190615.png) ### Choose chart type - Supported: line, area, column, bar, pie, donut, funnel, scatter, etc. - Required fields vary by chart type. First confirm column names and types in “Data query → View data”. ### Field mapping - Line/area/column/bar: - `xField`: dimension (date, category, region) - `yField`: measure (aggregated numeric value) - `seriesField` (optional): series grouping (for multiple lines/groups) - Pie/donut: - `Category`: categorical dimension - `Value`: measure - Funnel: - `Category`: stage/category - `Value`: value (usually count or percentage) - Scatter: - `xField`, `yField`: two measures or dimensions for axes > For more chart options, refer to the ECharts docs: [Axis](https://echarts.apache.org/handbook/en/concepts/axis) and [Examples](https://echarts.apache.org/examples/en/index.html) **Notes:** - After changing dimensions or measures, recheck the mapping to avoid empty or misaligned charts. - Pie/donut and funnel must provide a “category + value” combination. ### Common properties ![20251026191332](https://static-docs.nocobase.com/20251026191332.png) - Stack, smooth (line/area) - Labels, tooltip, legend - Axis label rotation, split lines - Pie/donut radius and inner radius, funnel sort order **Recommendations:** - Use line/area for time series with moderate smoothing; use column/bar for category comparison. - With dense data, avoid showing all labels to prevent overlap. ## Custom mode Return a full ECharts `option`. Suitable for advanced customization such as merging multiple series, complex tooltips, and dynamic styles. Recommended approach: consolidate data in `dataset.source`. For details, see ECharts docs: [Dataset](https://echarts.apache.org/handbook/en/concepts/dataset/#map-row-or-column-of-dataset-to-series) ![20251026191728](https://static-docs.nocobase.com/20251026191728.png) ### Data context - `ctx.data.objects`: array of objects (each row as an object, recommended) - `ctx.data.rows`: 2D array (with header) - `ctx.data.columns`: 2D array grouped by columns ### Example: monthly orders line chart ```js return { dataset: { source: ctx.data.objects || [] }, xAxis: { type: 'category' }, yAxis: {}, series: [ { type: 'line', smooth: true, showSymbol: false, }, ], } ``` ### Preview and Save - In Custom mode, after editing, click the Preview button on the right to update the chart preview. - At the bottom, click Save to apply and persist; click Cancel to revert all changes made this time. ![20251026192816](https://static-docs.nocobase.com/20251026192816.png) > [!TIP] > For more on chart options, see Advanced — Custom chart configuration. --- url: /data-visualization/guide/preview-and-save.md --- # Preview and Save - Preview: Temporarily render changes from the configuration panel into the page chart to verify the result. - Save: Persist changes from the configuration panel to the database. ## Entry points ![clipboard-image-1761479218](https://static-docs.nocobase.com/clipboard-image-1761479218.png) - In visual (Basic) mode, changes are applied to the preview automatically by default. - In SQL and Custom modes, click the Preview button on the right to apply changes to the preview. - A unified Preview button is available at the bottom of the configuration panel. ## Preview behavior - Temporarily displays the configuration on the page without writing to the database. After a refresh or cancel, the preview result is not retained. - Built‑in debounce: multiple refresh triggers in a short time only execute the latest one to avoid frequent requests. - Clicking Preview again overwrites the last preview result. ## Error messages - Query errors or validation failures: shown in the View data area. - Chart configuration errors (missing Basic mapping, exceptions from Custom JS): shown in the chart area or console while keeping the page operable. - Confirm column names and data types in View data before field mapping or writing Custom code to reduce errors. ## Save and Cancel - Save: write current changes into the block configuration and apply them to the page immediately. - Cancel: discard current unsaved changes and revert to the last saved state. - Save scope: - Data query: Builder parameters; in SQL mode, the SQL text is saved as well. - Chart options: Basic type, field mapping, and properties; Custom JS text. - Interaction events: JS text and binding logic. - After saving, the block takes effect for all visitors (subject to page permissions). ## Recommended flow - Configure data query → Run query → View data to confirm column names and types → Configure chart options to map core fields → Preview to validate → Save to apply. --- url: /data-visualization/guide/context-variables.md --- # Use context variables With context variables, you can reuse information from the current page, user, time, and filter inputs to render charts and enable linkage based on context. ## Applicable scope - Data query in Builder mode: select variables for filter conditions. ![clipboard-image-1761486073](https://static-docs.nocobase.com/clipboard-image-1761486073.png) - Data query in SQL mode: choose variables and insert expressions (for example, `{{ ctx.user.id }}`). ![clipboard-image-1761486145](https://static-docs.nocobase.com/clipboard-image-1761486145.png) - Chart options in Custom mode: write JS expressions directly. ![clipboard-image-1761486604](https://static-docs.nocobase.com/clipboard-image-1761486604.png) - Interaction events (for example, click to open a drill‑down dialog and pass data): write JS expressions directly. ![clipboard-image-1761486683](https://static-docs.nocobase.com/clipboard-image-1761486683.png) **Note:** - Do not wrap `{{ ... }}` with single or double quotes; binding is handled safely based on variable type (string, number, time, NULL). - When a variable is `NULL` or undefined, handle nulls explicitly in SQL using `COALESCE(...)` or `IS NULL`. --- url: /data-visualization/guide/filters-and-linkage.md --- # Page filters and linkage The page filter (filter block) provides a unified input for filter conditions at the page level and merges them into chart queries to keep multiple charts filtered consistently and linked. ## Feature overview - Add a filter block to the page to provide a unified filter entry for all charts. - Use “Filter”, “Reset”, and “Collapse” buttons to apply, clear, and collapse. - If the filter selects fields associated with a chart, their values are automatically merged into the chart query and trigger a refresh. - Filters can define custom fields and register them in context variables so they can be referenced in charts, tables, forms, and other data blocks. ![clipboard-image-1761487702](https://static-docs.nocobase.com/clipboard-image-1761487702.png) For more on using page filters and linking with charts or other data blocks, see the page filter documentation. ## Use page filter values in chart queries - Builder mode (recommended) - Auto merge: When the data source and collection match, you do not need to write variables in the chart query; page filters are merged with `$and`. - Manual selection: You can also select values from filter block custom fields in chart filter conditions. - SQL mode (via variable injection) - In SQL, use “Choose variable” to insert values from filter block custom fields. --- url: /data-visualization/guide/sql-data-query.md --- # Query data in SQL mode In the Data query panel, switch to SQL mode, write and run the query, and use the returned result directly for chart mapping and rendering. ![20251027075805](https://static-docs.nocobase.com/20251027075805.png) ## Write SQL statements - In the Data query panel, choose SQL mode. - Enter SQL and click Run query. - Supports complex statements including multi‑table JOIN and VIEW. Example: order amount by month ```sql SELECT TO_CHAR(order_date, 'YYYY-MM') as mon, SUM(total_amount) AS total FROM "order" GROUP BY mon ORDER BY mon ASC LIMIT 100; ``` ## View results - Click View data to open the preview panel. ![20251027080014](https://static-docs.nocobase.com/20251027080014.png) Data supports pagination; you can switch between Table and JSON to check column names and types. ![20251027080100](https://static-docs.nocobase.com/20251027080100.png) ## Field mapping - In Chart options, map fields based on the query result columns. - By default, the first column is used as the dimension (X axis or category), and the second column as the measure (Y axis or value). Mind the column order in SQL: ```sql SELECT TO_CHAR(order_date, 'YYYY-MM') as mon, -- dimension field in the first column SUM(total_amount) AS total -- measure field afterwards ``` ![clipboard-image-1761524022](https://static-docs.nocobase.com/clipboard-image-1761524022.png) ## Use context variables Click the x button at the top‑right of the SQL editor to choose context variables. ![20251027081752](https://static-docs.nocobase.com/20251027081752.png) After confirming, the variable expression is inserted at the cursor (or replaces the selected text). For example, `{{ ctx.user.createdAt }}`. Do not add extra quotes. ![20251027081957](https://static-docs.nocobase.com/20251027081957.png) ## More examples For more examples, see the NocoBase [Demo app](https://demo3.sg.nocobase.com/admin/5xrop8s0bui) **Recommendations:** - Stabilize column names before mapping to charts to avoid errors later. - During debugging, set `LIMIT` to reduce returned rows and speed up preview. ## Preview, save, and rollback - Click Run query to request data and refresh the chart preview. - Click Save to persist the current SQL text and related configuration to the database. - Click Cancel to revert to the last saved state and discard current unsaved changes. --- url: /data-visualization/guide/custom-chart-options.md --- # Custom chart configuration In Custom mode, configure charts by writing JS in the editor. Based on `ctx.data`, return a complete ECharts `option`. This suits merging multiple series, complex tooltips, and dynamic styles. In principle, all ECharts features and chart types are supported. ![clipboard-image-1761524637](https://static-docs.nocobase.com/clipboard-image-1761524637.png) ## Data context - `ctx.data.objects`: array of objects (each row as an object) - `ctx.data.rows`: 2D array (with header) - `ctx.data.columns`: 2D array grouped by columns **Recommended usage:** Consolidate data in `dataset.source`. For detailed usage, please refer to the ECharts documentation: [Dataset](https://echarts.apache.org/handbook/en/concepts/dataset/#map-row-or-column-of-dataset-to-series) [Axis](https://echarts.apache.org/handbook/en/concepts/axis) [Examples](https://echarts.apache.org/examples/en/index.html) Let’s start with a simple example. ## Example 1: Monthly order bar chart ![20251027082816](https://static-docs.nocobase.com/20251027082816.png) ```js return { dataset: { source: ctx.data.objects || [] }, xAxis: { type: 'category' }, yAxis: {}, series: [ { type: 'bar', showSymbol: false, }, ], } ``` ## Example 2: Sales trend chart ![clipboard-image-1761525188](https://static-docs.nocobase.com/clipboard-image-1761525188.png) ```js return { dataset: { source: ctx.data.objects.reverse() }, title: { text: "Monthly Sales Trend", subtext: "Last 12 Months", left: "center" }, tooltip: { trigger: "axis", axisPointer: { type: "cross" } }, legend: { data: ["Revenue", "Order Count", "Avg Order Value"], bottom: 0 }, grid: { left: "5%", right: "5%", bottom: "60", top: "80", containLabel: true }, xAxis: { type: "category", boundaryGap: false, axisLabel: { rotate: 45 } }, yAxis: [ { type: "value", name: "Amount(¥)", position: "left", axisLabel: { formatter: (value) => { return (value/10000).toFixed(0) + '0k'; } } }, { type: "value", name: "Order Count", position: "right" } ], series: [ { name: "Revenue", type: "line", smooth: true, encode: { x: "month", y: "monthly_revenue" }, areaStyle: { opacity: 0.3 }, itemStyle: { color: "#5470c6" } }, { name: "Order Count", type: "bar", yAxisIndex: 1, encode: { x: "month", y: "order_count" }, itemStyle: { color: "#91cc75", opacity: 0.6 } }, { name: "Avg Order Value", type: "line", encode: { x: "month", y: "avg_order_value" }, itemStyle: { color: "#fac858" }, lineStyle: { type: "dashed" } } ] } ``` **Recommendations:** - Keep a pure function style: generate `option` only from `ctx.data` and avoid side effects. - Changes to query column names affect indexing; standardize names and confirm in "View data" before editing code. - For large datasets, avoid complex synchronous computations in JS; aggregate during the query stage when necessary. ## More examples For more usage examples, you can refer to the NocoBase [Demo app](https://demo3.sg.nocobase.com/admin/5xrop8s0bui). You can also browse the official ECharts [Examples](https://echarts.apache.org/examples/en/index.html) to find your desired chart effect, then reference and copy the JS configuration code. ## Preview and Save ![20251027083938](https://static-docs.nocobase.com/20251027083938.png) - Click "Preview" on the right side or at the bottom to refresh the chart and validate the JS configuration. - Click "Save" to persist the current JS configuration to the database. - Click "Cancel" to revert to the last saved state. --- url: /data-visualization/guide/chart-events.md --- # Custom interaction events Write JS in the events editor and register interactions via the ECharts instance `chart` to enable linkage, such as navigating to a new page or opening a drill-down dialog. ![clipboard-image-1761489617](https://static-docs.nocobase.com/clipboard-image-1761489617.png) ## Register and Unregister - Register: `chart.on(eventName, handler)` - Unregister: `chart.off(eventName, handler)` or `chart.off(eventName)` to clear events by name **Note:** For safety, it's strongly recommended to unregister an event before registering it again! ## Handler params structure ![20251026222859](https://static-docs.nocobase.com/20251026222859.png) Common fields include `params.data` and `params.name`. ## Example: click to highlight selection ```js chart.off('click'); chart.on('click', (params) => { const { seriesIndex, dataIndex } = params; // Highlight the current data point chart.dispatchAction({ type: 'highlight', seriesIndex, dataIndex }); // Downplay others chart.dispatchAction({ type: 'downplay', seriesIndex }); }); ``` ## Example: click to navigate ```js chart.off('click'); chart.on('click', (params) => { const order_date = params.data[0] // Option 1: internal navigation without full page refresh (recommended), only need relative path ctx.router.navigate(`/new-path/orders?order_date=${order_date}`) // Option 2: navigate to external page, full URL required window.location.href = `https://www.host.com/new-path/orders?order_date=${order_date}` // Option 3: open external page in a new tab, full URL required window.open(`https://www.host.com/new-path/orders?order_date=${order_date}`) }); ``` ## Example: click to open details dialog (drill-down) ```js chart.off('click'); chart.on('click', (params) => { ctx.openView(ctx.model.uid + '-1', { mode: 'dialog', size: 'large', defineProperties: {}, // register context variables for the new dialog }); }); ``` ![clipboard-image-1761490321](https://static-docs.nocobase.com/clipboard-image-1761490321.png) In the newly opened dialog, use chart context variables via `ctx.view.inputArgs.XXX`. ## Preview and Save - Click "Preview" to load and execute the event code. - Click "Save" to persist the current event configuration. - Click "Cancel" to revert to the last saved state. **Recommendations:** - Always use `chart.off('event')` before binding to avoid duplicate executions or increased memory usage. - Use lightweight operations (e.g., `dispatchAction`, `setOption`) inside event handlers to avoid blocking the rendering process. - Validate against chart options and data queries to ensure that the fields handled in the event are consistent with the current data. --- url: /guide/index.md --- --- url: /solution/crm/guide/guide-overview.md --- # System Overview & Dashboards > This chapter covers the two main dashboards — Analytics (data analysis center) and Overview (daily workspace). ## System Overview CRM 2.0 is a complete sales management system covering the entire process from lead acquisition to order delivery. After logging in, the top menu bar is your main navigation entry. ### Multi-language & Themes The system supports language switching (top-right corner). All JS blocks and charts are adapted for multiple languages. Both light and dark themes are supported, but we currently **recommend light theme + compact mode** for higher information density. Some display issues in dark mode will be fixed in future updates. ![00-overview-2026-04-01-23-38-28](https://static-docs.nocobase.com/00-overview-2026-04-01-23-38-28.png) --- ## Analytics — Data Analysis Center Analytics is the first page in the menu bar and the first screen you see when opening the system. ### Global Filters At the top of the page, there is a filter bar with **Date Range** and **Owner** filter conditions. After filtering, all charts on the page refresh in sync. ![00-overview-2026-04-01-23-40-45](https://static-docs.nocobase.com/00-overview-2026-04-01-23-40-45.png) ### KPI Cards Below the filter bar are 4 KPI cards: | Card | Description | Click Behavior | |------|-------------|----------------| | **Total Revenue** | Cumulative revenue amount | Popup: payment status pie chart + monthly revenue trend | | **New Leads** | Number of new leads in the period | Redirect to Leads page, auto-filtered to "New" status | | **Conversion Rate** | Lead-to-deal conversion ratio | Popup: stage distribution pie chart + amount bar chart | | **Avg Deal Cycle** | Average days from creation to close | Popup: cycle distribution + monthly won trend | Each card supports **click-through drill-down** — popups show more detailed analysis charts. With customization capability, you can drill further (company → department → individual). ![00-overview-2026-04-01-23-42-33](https://static-docs.nocobase.com/00-overview-2026-04-01-23-42-33.gif) :::tip[Data looks reduced after clicking?] When you click a KPI card to jump to a list page, the URL carries filter parameters (e.g., `?status=new`). If you notice fewer records, it's because this parameter is still active. Navigate back to the dashboard and re-enter the list page to restore full data. ::: ![00-overview-2026-04-01-23-44-19](https://static-docs.nocobase.com/00-overview-2026-04-01-23-44-19.png) ### Charts Area Below the KPIs are 5 core charts: | Chart | Type | Description | Click Behavior | |-------|------|-------------|----------------| | **Opportunity Stage Distribution** | Bar chart | Count, amount, and weighted probability per stage | Popup: drill-through by customer/owner/month | | **Sales Funnel** | Funnel | Lead → Opportunity → Quotation → Order conversion | Click to jump to corresponding entity page | | **Monthly Sales Trend** | Bar + Line | Monthly revenue, order count, average order value | Jump to Orders page (with month parameter) | | **Customer Growth Trend** | Bar + Line | Monthly new customers, cumulative total | Jump to Customers page | | **Industry Distribution** | Pie chart | Customer distribution by industry | Jump to Customers page | ![00-overview-2026-04-01-23-46-36](https://static-docs.nocobase.com/00-overview-2026-04-01-23-46-36.png) #### Sales Funnel Shows the conversion rate across the full pipeline: Lead → Opportunity → Quotation → Order. Each layer is clickable, redirecting to the corresponding entity list page (e.g., clicking the Opportunity layer → jumps to the opportunity list). #### Monthly Sales Trend Bar chart shows monthly revenue, with line overlays for order count and average order value. Clicking a month's bar → jumps to the Orders page with that month's time filter pre-applied (e.g., `?month=2026-02`), showing that month's order details directly. #### Customer Growth Trend Bar chart shows monthly new customer count, line shows cumulative total. Clicking a month's bar → jumps to Customers page filtered to that month's new customers. #### Industry Distribution Pie chart shows customer distribution by industry with associated order amounts. Clicking an industry segment → jumps to Customers page filtered to that industry. ### Opportunity Stage Drill-through Clicking a stage bar in the Opportunity Stage Distribution chart opens a deep analysis popup for that stage: - **Monthly trend**: Monthly changes for opportunities in this stage - **By owner**: Who is working on these opportunities - **By customer**: Which customers have opportunities in this stage - **Bottom summary**: Select customers to view cumulative amounts ![00-overview-2026-04-01-23-49-04](https://static-docs.nocobase.com/00-overview-2026-04-01-23-49-04.png) Each stage (Prospecting / Analysis / Proposal / Negotiation / Won / Lost) has different drill-through content, reflecting the focus areas of each stage. The core question this chart answers: **Where in the funnel is the most drop-off?** If the Proposal stage has many opportunities piling up but few moving to Negotiation, it suggests a problem in the quotation process. ![00-overview-2026-04-01-23-48-21](https://static-docs.nocobase.com/00-overview-2026-04-01-23-48-21.gif) ### Chart Configuration (Advanced) Each chart has three configuration dimensions: 1. **SQL Data Source**: Determines what data the chart displays; you can verify queries in the SQL builder 2. **Chart Style**: JSON configuration in the customization area, controlling chart appearance 3. **Events**: Click behavior (popup OpenView / page redirect) ![00-overview-2026-04-01-23-51-00](https://static-docs.nocobase.com/00-overview-2026-04-01-23-51-00.png) ### Filter Linkage When any filter condition in the top filter bar is changed, **all charts on the page refresh simultaneously** — no need to set filters individually. Typical usage: - **View someone's performance**: Select an Owner → all page data switches to that person's leads, opportunities, and orders - **Compare time periods**: Switch date range from "This Month" to "This Quarter" → trend chart ranges update in sync The linkage between filter bar and charts is implemented through **page event flows** — form variables are injected before rendering, and charts reference filter values through variables in their SQL. ![00-overview-2026-04-01-23-52-29](https://static-docs.nocobase.com/00-overview-2026-04-01-23-52-29.png) ![00-overview-2026-04-01-23-53-57](https://static-docs.nocobase.com/00-overview-2026-04-01-23-53-57.png) :::note SQL templates currently only support `if` syntax for conditional logic. We recommend referencing existing templates in the system, or using AI to assist with modifications. ::: --- ## Overview — Daily Workspace Overview is the second dashboard page, focused on daily operations rather than data analysis. The core question it answers: **What should I do today? Which leads are worth following up on?** ![00-overview-2026-04-01-23-56-07](https://static-docs.nocobase.com/00-overview-2026-04-01-23-56-07.png) ### Top Leads Automatically filters leads with AI score ≥ 75 and status New / Working (Top 5), showing for each: - **AI Score Gauge**: Circular gauge visually showing lead quality (green = high score = worth prioritizing) - **AI Recommended Next Step**: System auto-recommends follow-up actions based on lead characteristics (e.g., "Schedule a demo") - **Lead Basic Info**: Name, company, source, creation time Click a lead name to jump to its details, or click "View All" to go to the leads list page. A quick glance at this table each morning tells you who to contact first. ![00-overview-2026-04-01-23-56-36](https://static-docs.nocobase.com/00-overview-2026-04-01-23-56-36.png) ### Today's Tasks A list of today's activities (meetings, calls, tasks, etc.), supporting: - **One-click complete**: Click "Done" to mark a task complete; it turns gray - **Overdue reminder**: Unfinished overdue tasks are highlighted in red - **View details**: Click the task name to open details - **Create new**: Add a new activity record directly here ![00-overview-2026-04-01-23-57-09](https://static-docs.nocobase.com/00-overview-2026-04-01-23-57-09.png) ### Activity Calendar FullCalendar view with activities color-coded by type (meetings/calls/tasks/emails/notes). Supports month/week/day switching, drag-to-reschedule, and click-to-view details. ![00-overview-2026-04-01-23-58-02](https://static-docs.nocobase.com/00-overview-2026-04-01-23-58-02.gif) --- ## Other Dashboards (More Charts) Three additional dashboards for different roles are available in the menu. They are provided as references and can be hidden as needed: | Dashboard | Target User | Features | |-----------|-------------|----------| | **Sales Manager** | Sales team leads | Team leaderboard, risk scatter plot, monthly targets | | **Sales Rep** | Individual reps | Data auto-filtered by current user; shows only your own performance | | **Executive** | VP Sales / C-Suite | Revenue forecast, customer health, Win/Loss trends | ![00-overview-2026-04-01-23-58-39](https://static-docs.nocobase.com/00-overview-2026-04-01-23-58-39.png) Dashboards you don't need can be hidden from the menu without affecting system functionality. ![00-overview-2026-04-02-00-02-39](https://static-docs.nocobase.com/00-overview-2026-04-02-00-02-39.png) --- ## KPI Drill-through You may have noticed that almost every number and chart above is "clickable." This is the core interaction pattern in the CRM — **KPI drill-through**: click a summary number → see the detailed data behind it. Drill-through comes in two forms: | Form | Use Case | Example | |------|----------|---------| | **Popup drill-through** | Multi-dimensional analysis | Click "Total Revenue" → popup shows pie chart + trends | | **Page redirect** | View and operate on detail records | Click "New Leads" → jump to Leads list | **Example**: In the Analytics "Monthly Sales Trend" chart, you notice February's revenue bar is notably low → click that bar → the system jumps to the Orders page with `month = 2026-02` pre-applied → you immediately see all February order details to investigate further. > The dashboard isn't just for viewing — it's the system's navigation hub. Every number is an entry point, guiding you from macro to micro, layer by layer to the root cause. --- After understanding the system layout and dashboards, go back to the [User Guide](./) for subsequent chapters. ## Related Pages - [CRM User Guide](./) - [Solution Overview](../index) - [Installation](../installation) --- url: /solution/crm/guide/index.md --- # User Guide > This guide walks you through the core CRM sales process from start to finish. We recommend reading in order — each chapter builds on the previous one. ## Reading Order The core business flow is **Lead → Customer/Contact → Opportunity → Quotation → Order**. This guide follows that path: | Chapter | Content | What You'll Learn | |---------|---------|-------------------| | [System Overview & Dashboards](./guide-overview) | System overview, Analytics dashboard, Overview workspace | Menu structure, KPI drill-through, chart linkage | | Lead Management | Creating, filtering, AI scoring, follow-up, and conversion | How to efficiently identify high-quality leads and convert them to customers + opportunities | | Opportunities & Quotations | Kanban board, stage progression, quotation creation, approval workflow | How to move deals through the sales funnel and complete the approval process | | Order Management | Quotation to order, status tracking, payment monitoring | How to close the loop from quotation to delivery | | Customer Management | Customer 360 view, health scores, customer merge | How to manage the full customer lifecycle | | AI Employees | AI button usage, scenario examples | How to use AI to assist daily sales work | | Emails & Contacts | Email send/receive, contact management, CRM linking | How to handle customer communication within the CRM | ## Prerequisites - CRM 2.0 installed and running (see [Installation](../installation)) - Logged in with an admin account ## System Navigation After logging in, the top menu bar is your main navigation: ``` 📁 Dashboards Analytics Overview 📁 More Charts Executive Sales Rep Sales Manager Leads Customers Opportunities Products Orders 📁 Settings Contacts Activity Exchange Rate Stage Settings Emails Data Analysis ``` **Dashboards → Analytics** is the first page you see every day — it summarizes core KPIs, sales funnel, and trend charts to give you a quick overview of business performance. Ready? Let's start with [System Overview & Dashboards](./guide-overview). --- url: /data-sources/development/index.md --- # Data Source Extension :::tip Content to be added ::: --- url: /data-sources/file-manager/development/index.md --- # Extension Development ## Extending Frontend File Types For uploaded files, the client UI can display different previews based on file types. The attachment field of the file manager uses a built-in browser-based (iframe) file preview, supporting most file types (such as images, videos, audio, and PDFs) for direct preview in the browser. When a file type is not supported for browser preview or requires special interaction, additional preview components can be extended based on the file type. ### Example For example, if you want to extend a carousel component for image files, you can use the following code: ```ts import match from 'mime-match'; import { Plugin, attachmentFileTypes } from '@nocobase/client'; class MyPlugin extends Plugin { load() { attachmentFileTypes.add({ match(file) { return match(file.mimetype, 'image/*'); }, Previewer({ index, list, onSwitchIndex }) { const onDownload = useCallback( (e) => { e.preventDefault(); const file = list[index]; saveAs(file.url, `${file.title}${file.extname}`); }, [index, list], ); return ( onSwitchIndex(null)} onMovePrevRequest={() => onSwitchIndex((index + list.length - 1) % list.length)} onMoveNextRequest={() => onSwitchIndex((index + 1) % list.length)} imageTitle={list[index]?.title} toolbarButtons={[ , ]} /> ); }, }); } } ``` The `attachmentFileTypes` is an entry object provided by the `@nocobase/client` package for extending file types. You can use its `add` method to extend a file type descriptor. Each file type must implement a `match()` method to check if the file type meets the requirements. In the example, the `mime-match` package is used to check the file's `mimetype` attribute. If it matches `image/*`, it is considered a file type that needs processing. If it does not match, it will fall back to the built-in type. The `Previewer` property on the type descriptor is the component used for previewing. When the file type matches, this component will be rendered for preview. It is generally recommended to use a modal component (like ``) as the base container and place the preview and interactive content within that component to implement the preview functionality. ### API ```ts export interface FileModel { id: number; filename: string; path: string; title: string; url: string; extname: string; size: number; mimetype: string; } export interface PreviewerProps { index: number; list: FileModel[]; onSwitchIndex(index): void; } export interface AttachmentFileType { match(file: any): boolean; Previewer?: React.ComponentType; } export class AttachmentFileTypes { add(type: AttachmentFileType): void; } ``` #### `attachmentFileTypes` `attachmentFileTypes` is a global instance imported from the `@nocobase/client` package: ```ts import { attachmentFileTypes } from '@nocobase/client'; ``` #### `attachmentFileTypes.add()` Registers a new file type descriptor with the file type registry. The descriptor's type is `AttachmentFileType`. #### `AttachmentFileType` ##### `match()` A method for matching file formats. The `file` parameter is a data object for the uploaded file, containing properties that can be used for type checking: * `mimetype`: The file's mimetype. * `extname`: The file extension, including the `.`. * `path`: The relative storage path of the file. * `url`: The file's URL. Returns a `boolean` indicating whether the file is a match. ##### `Previewer` A React component for previewing the file. Props: * `index`: The index of the file in the attachment list. * `list`: The list of attachments. * `onSwitchIndex`: A function to switch the previewed file by its index. The `onSwitchIndex` function can be called with any index from the `list` to switch to another file. Calling it with `null` closes the preview component. ```ts onSwitchIndex(null); ``` --- url: /development/index.md --- --- url: /file-manager/development/index.md --- # Extension Development ## Extending Storage Engines ### Server-side 1. **Inherit `StorageType`** Create a new class and implement the `make()` and `delete()` methods, and override hooks like `getFileURL()`, `getFileStream()`, and `getFileData()` if necessary. Example: ```ts // packages/my-plugin/src/server/storages/custom.ts import { AttachmentModel, StorageModel, StorageType } from '@nocobase/plugin-file-manager'; import type { StorageEngine } from 'multer'; import multer from 'multer'; import path from 'path'; import fs from 'fs/promises'; export class CustomStorageType extends StorageType { make(): StorageEngine { return multer.diskStorage({ destination: path.resolve('custom-uploads'), filename: (req, file, cb) => { cb(null, `${Date.now()}-${file.originalname}`); }, }); } async delete(records: AttachmentModel[]) { let deleted = 0; const failures: AttachmentModel[] = []; for (const record of records) { try { await fs.unlink(path.resolve('custom-uploads', record.path || '', record.filename)); deleted += 1; } catch (error) { failures.push(record); } } return [deleted, failures]; } } ``` 4. **Register the new type** Inject the new storage implementation in the plugin's `beforeLoad` or `load` lifecycle: ```ts // packages/my-plugin/src/server/plugin.ts import { Plugin } from '@nocobase/server'; import PluginFileManagerServer from '@nocobase/plugin-file-manager'; import { CustomStorageType } from './storages/custom'; export default class MyStoragePluginServer extends Plugin { async load() { const fileManager = this.app.pm.get(PluginFileManagerServer); fileManager.registerStorageType('custom-storage', CustomStorageType); } } ``` After registration, the storage configuration will appear in the `storages` resource just like built-in types. The configuration provided by `StorageType.defaults()` can be used to auto-fill forms or initialize default records. ## Extending Frontend File Types For uploaded files, different preview content can be displayed on the frontend interface based on different file types. The file manager's attachment field has a built-in browser-based file preview (embedded in an iframe), which supports previewing most file formats (such as images, videos, audio, and PDFs) directly in the browser. When a file format is not supported by the browser for preview, or when special preview interactions are required, you can extend the file type-based preview component. ### Example For example, if you want to integrate a custom online preview for Office files, you can use the following code: ```tsx import React, { useMemo } from 'react'; import { Plugin, matchMimetype } from '@nocobase/client'; import { filePreviewTypes } from '@nocobase/plugin-file-manager/client'; class MyPlugin extends Plugin { load() { filePreviewTypes.add({ match(file) { return matchMimetype(file, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); }, Previewer({ file }) { const url = useMemo(() => { const src = file.url.startsWith('https://') || file.url.startsWith('http://') ? file.url : `${location.origin}/${file.url.replace(/^\//, '')}`; const u = new URL('https://view.officeapps.live.com/op/embed.aspx'); u.searchParams.set('src', src); return u.href; }, [file.url]); return ## 1.1 Quick Demo We recommend starting with a quick demo of NocoBase to experience its powerful features. You can enter your email and other details in the [**Online Demo**](https://demo.nocobase.com/new) and click to open an account. This grants you a two-day trial with access to all commercial plugins: ![](https://static-docs.nocobase.com/Solution/202412141613291734164009.png) ![](https://static-docs.nocobase.com/Solution/202412141612451734163965.png) **After receiving the official NocoBase email, you can begin exploring the system without any concerns, gaining hands-on experience with its flexibility and capabilities.** ## 1.2 Basic Interface of NocoBase **Welcome to NocoBase! If you’re new, the interface may feel unfamiliar, and you may not know where to start. Don’t worry; let’s familiarize ourselves with the main functional areas step-by-step to help you get started quickly.** ### 1.2.1 **Interface Configuration** Upon first entering NocoBase, you’ll see a clean and intuitive main interface. The [**Interface Configuration**](https://docs.nocobase.com/handbook/ui/ui-editor) button is located in the upper right corner; click this to enter configuration mode, which is your primary workspace for building system pages. ![Interface Configure Mode](https://static-docs.nocobase.com/Solution/401734164740202414162512.png) **Steps to Operate:** 1. **Enter Configuration Mode**: Click the "Interface Configuration" button in the upper right to enter this mode. 2. **Add a [Menu](https://docs.nocobase.com/handbook/ui/menus) Page**: * **Click “Add Menu Item.”** * **Enter a menu name (e.g., “Test Page”), then confirm.** * **The system will automatically create and navigate to the new test page.** ![](https://static-docs.nocobase.com/Solution/demoE3v1-01.gif) 3. **Create a [Block](https://docs.nocobase.com/handbook/ui/blocks)**: * **On the test page, click the “Create Block” button.** * **Select a data block type, such as “Table Block.”** * **Connect it to a data table, like the built-in “Users” table.** * **Choose the fields you want to display, and confirm.** ![Create A Block](https://static-docs.nocobase.com/Solution/demoE3v1-02.gif) **NocoBase’s block design draws inspiration from Notion but offers more powerful functionality, supporting the creation of more complex systems. In the following tutorials, we’ll dive deeper into the capabilities of various block types.** ### 1.2.2 **Plugin Manager** Plugins are essential tools for extending NocoBase’s functionality. In the [**Plugin Manager**](https://docs.nocobase.com/handbook/plugin-manager), you can view, install, enable, or disable various plugins to meet different business needs. **By using plugin extensions, you can achieve convenient or unexpected integrations, facilitating creation and development even further.** ![Plugin Manager](https://static-docs.nocobase.com/Solution/241734166164202414164912.png) **Steps to Operate:** 1. **View Installed Plugins**: Click “Plugin Manager” to view all currently installed plugins. 2. **Activate a Plugin**: * **Find the plugin you need, such as the “Theme Editor” plugin.** * **Click the “Enable” button to activate it.** ![Theme Editor](https://static-docs.nocobase.com/Solution/demoE3v1-03.gif) 3. **Test Plugin Functionality**: * **Once activated, you can adjust the system theme from the Theme Editor interface, including colors, fonts, etc., for personalized customization.** ![Theme Editor](https://static-docs.nocobase.com/Solution/demoE3v1-04.gif) * **In the Settings Center, you will see the Theme Editor interface, where you can personalize the system theme, such as changing colors, fonts, etc.** ![Theme Editor Interface](https://static-docs.nocobase.com/Solution/111734167291202414170812.png) ### 1.2.3 **Settings Page** The **Settings Page** integrates settings options for the system and some plugins, helping you comprehensively manage all aspects of NocoBase. ![Setting Page](https://static-docs.nocobase.com/Solution/571734167457202414171012.png) **Some commonly used plugin configuration options include:** * **[Data Source Management](https://docs.nocobase.com/handbook/data-source-manager)**: Manage all data tables and configure primary or external databases. * **[System Settings](https://docs.nocobase.com/handbook/system-settings)** : Modify basic information like system name, logo, and language. * **[User and Permissions](https://docs.nocobase.com/handbook/users)**: Manage user accounts and configure permissions for different roles. * **[Plugin Settings](https://docs.nocobase.com/handbook/plugin-manager)**: Configure and manage installed plugins in detail. ### 1.2.4 **Version Information and Support** In the upper right corner of the interface, you can see **NocoBase’s version information**. If you encounter any issues during use, visit the **homepage** and **user manual** for assistance. ![Version Info](https://static-docs.nocobase.com/Solution/371734167497202414171112.png) ### 1.2.5 **Personal Center Menu** The Personal Center menu, located in the top right, allows you to **modify personal information**, **switch roles**, and perform some important system operations. Some plugins may also extend functionality in this area. ![Personal Center Menu](https://static-docs.nocobase.com/Solution/541734167574202414171212.png) ## 1.3 Installing NocoBase **If you decide to dive deeper into using NocoBase, you’ll need to install it on your computer or server. NocoBase offers several installation methods, allowing you to easily embark on a no-code development journey.** ### 1.3.1 **Installation Methods** 1. **Docker Installation (Recommended)** * **Advantages**: Fast and simple, suitable for users familiar with Docker. * **Version Selection**: * **main & latest version**: The most stable version to date, suitable for most users. * **next version**: Beta version for users interested in new features; note that it may not be fully stable. * **Steps**: * **Follow the** [official installation guide](https://docs.nocobase.com/welcome/getting-started/installation/docker-compose) to deploy NocoBase with Docker Compose. 2. **Create-NocoBase-App Installation** * **Suitable for**: Frontend developers or users familiar with npm. * **Steps**: * **Refer to the** [installation guide](https://docs.nocobase.com/welcome/getting-started/installation/create-nocobase-app) to install using the npm package. 3. **Source Code Installation** * **Suitable for**: Developers looking to deeply customize NocoBase. * **Steps**: * **Clone the source code from GitHub and install as per the custom requirements using the** [installation guide](https://docs.nocobase.com/welcome/getting-started/installation/git-clone). ### 1.3.2 **Detailed Installation Guide (Using Docker as an Example)** You can find detailed steps and instructions for any installation method in the **NocoBase installation documentation**. Below is a brief guide for Docker installation to help you get started quickly: 1. **Install Docker**: Ensure Docker is installed on your system. If not, visit the [Docker website](https://www.docker.com/) to download and install. 2. **Obtain Docker Compose File**: * **Open a terminal or command line tool.** * **Create a NocoBase directory and Docker Compose configuration.** ``` mkdir nocobase cd nocobase vim docker-compose.yml ``` 3. **After entering**`docker-compose.yml`, paste the following configuration, adjust as needed, and save the file: ``` version: "3" networks: nocobase:   driver: bridge services: app:   image: registry.cn-shanghai.aliyuncs.com/nocobase/nocobase:latest   networks:     - nocobase   depends_on:     - postgres   environment:     - APP_KEY=your-secret-key     - DB_DIALECT=postgres     - DB_HOST=postgres     - DB_DATABASE=nocobase     - DB_USER=nocobase     - DB_PASSWORD=nocobase     - TZ=Asia/Shanghai   volumes:     - ./storage:/app/nocobase/storage   ports:     - "13000:80" postgres:   image: registry.cn-shanghai.aliyuncs.com/nocobase/postgres:16   restart: always   command: postgres -c wal_level=logical   environment:     POSTGRES_USER=nocobase     POSTGRES_DB=nocob ase     POSTGRES_PASSWORD=nocobase   volumes:     - ./storage/db/postgres:/var/lib/postgresql/data   networks:     - nocobase ``` 4. **Start NocoBase**: * **Run the following command in the NocoBase directory to start the service:** ``` docker-compose up -d ``` 5. **Access NocoBase**: * **Open a browser and visit** `http://localhost:13000` (depending on configuration) to see the NocoBase login screen. **After completing these steps, you will have successfully installed and launched NocoBase!** --- **In** [the next chapters (Chapter 2: Designing a Task Management System)](https://www.nocobase.com/en/tutorials/task-tutorial-system-design) we will further explore NocoBase’s powerful features, guiding you in building a feature-rich application. Let’s take the next step to unlock the potential of no-code development! --- url: /tutorials/v1/02-designing-system.md --- # Chapter 2: Designing the System Designing a task management system may sound complex, but with NocoBase, this process becomes straightforward and even enjoyable. We'll walk through defining requirements, designing the data structure, and planning future features, all step-by-step. Rest assured, we won't be wading through intimidating lines of code; instead, we’ll use the most intuitive and straightforward approach to build your task management system. ### 2.1 System Requirements Analysis Before we dive in, let's clarify the features this task management system should include. Consider how we typically manage tasks or what an ideal task management system might offer: - **Task Management**: Users can create, edit, and delete tasks, assign tasks to different people, and track task progress in real-time. - **Multiple Views**: Tasks can be displayed in various formats, such as a list view, Kanban board, Gantt chart, or calendar view for better visualization. - **Online Documentation**: Tasks should support online document editing, helping team members understand task details. - **Attachment Management**: Attachments like images, videos, and important records should be added to tasks as needed. - **Comments**: Team members can comment on tasks, share feedback, and document discussions. Below is a flowchart to outline how these functional modules relate to each other: ![](https://static-docs.nocobase.com/task_management20241106949.drawio.svg) Clearer now, right? --- > **Introduction to Collection:** NocoBase uses a "Collection" method to describe data structures, unifying data from various sources and providing a solid foundation for data management and analysis. > > It supports various types of collections including general, inheritance, tree, calendar, file, expression, SQL, view, and external collections, catering to diverse data handling needs. > > This design enhances the flexibility and efficiency of data operations. ### 2.2 Collections Design Now let’s dig into the design. To support these features, we need to plan the Collections for the system. Don’t worry; we won’t need a complex database structure—just a few simple tables. Based on our requirements analysis, we’ll design the following Collections: 1. **Users Collection**: Stores user information in the system. Who is working on the tasks? Who is managing them? 2. **Tasks Collection**: Contains detailed information for each task, including task name, document, assigned person, and progress status. 3. **Attachments Collection**: Stores all task-related attachments, such as images and files. 4. **Comments Collection**: Records user comments on tasks, allowing team members to interact. The relationships between these tables are straightforward: each task can have multiple attachments and comments, and all attachments and comments are created or uploaded by a user. This setup forms the core structure of our task management system. Here’s a diagram showing the basic relationships between these tables: ![](https://static-docs.nocobase.com/241219-2.svg) ### 2.3 Table Design in NocoBase So, how do we implement this task management system with NocoBase? It’s even simpler than you might think: - **Tasks Table**: This is the core of the entire system, storing detailed information for each task. - **Comments Table**: Stores comments related to tasks so that team members can provide feedback. More complex features, such as attachment management and user information, are already built into NocoBase, so there’s no need for manual setup. Much simpler, right? We’ll start with a basic task data management system and gradually expand its functionality. For example, we’ll first design the basic fields for tasks, then later add the comments feature. The whole process is flexible and easy to control. Our table structure will look something like this, covering all the fields we need: ![](https://static-docs.nocobase.com/241219-3.svg) ### Summary In this section, you’ve learned how to design a basic task management system. Using NocoBase, we started with requirements analysis and then planned the Collections and field structure. Next, you’ll find that implementing these features is even easier than designing them. For instance, the initial Tasks Table might be as simple as this: ```text Tasks Table: Task Name (task_name) - Single-line text Task Description (task_description) - Multi-line text ``` Pretty intuitive, right? Ready for the [next step? (Chapter 3: Task Data Management)](https://www.nocobase.com/en/tutorials/task-tutorial-data-management-guide) --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for support. See you in the next chapter! --- url: /tutorials/v1/03-task-data.md --- # Chapter 3: Task Data Management With the requirements of our task management system outlined, it’s time to dive into the practical steps! Recall that our system should support **[Creating](https://docs.nocobase.com/handbook/ui/actions/types/add-new)**, **[Editing](https://docs.nocobase.com/handbook/ui/actions/types/edit)**, and **[Deleting](https://docs.nocobase.com/handbook/ui/actions/types/delete)** tasks, along with **viewing the task list**. All of this functionality can be achieved using NocoBase’s pages, blocks, and actions. > For further details, see [Menus](https://docs.nocobase.com/handbook/ui/menus) and [Pages](https://docs.nocobase.com/handbook/ui/pages) in the official documentation. ### 3.1 How to Begin? You may remember that we previously covered how to create pages and display user lists. These pages act as canvases where you can arrange various blocks in any order and size. Here’s a quick recap of the steps: 1. [**Create a Page**](https://docs.nocobase.com/handbook/ui/pages): Just a few clicks, and your page is ready. ![Create a Page](https://static-docs.nocobase.com/Solution/demoE3v1-01.gif) 2. **Create a [Table Block](https://docs.nocobase.com/handbook/ui/blocks/data-blocks/table)**: Select a table block to display various data. ![Create a Table Block](https://static-docs.nocobase.com/Solution/demoE3v1-02.gif) Simple, right? But upon opening “Data List,” you might notice only the “User” and “Role”collections. Where is the task table? Don’t worry; it’s accessible through NocoBase’s [**Data Source**](https://docs.nocobase.com/handbook/data-source-manager) feature. > **About Data Sources:** A data source can be a database, API, or other data storage types. NocoBase supports various relational databases, including MySQL, PostgreSQL, SQLite, and MariaDB. > > NocoBase provides a **Data Source Management Plugin** for managing data sources and tables. However, to connect to data sources, you’ll also need specific **Data Source Plugins**. ### 3.2 Data Source: Your Collections Repository ![](https://static-docs.nocobase.com/20241009144356.png) In NocoBase, all collections are stored within [**Data Sources**](https://docs.nocobase.com/handbook/data-source-manager), which act like books containing the design and structure of each collection. Let’s add our new chapter by creating a **task collection**. > To explore more on data sources and collections, refer to [Data Source Management](https://docs.nocobase.com/handbook/data-source-manager) and [Collection Overview](https://docs.nocobase.com/handbook/data-modeling/collection). - **Accessing Data Source Settings**: - Click **Settings** > **Data Sources** > **Main Data Source Configuration**. - In the main data source, you’ll see existing collections, usually just “User” and “Role” by default. ![Data Source Settings](https://static-docs.nocobase.com/Solution/demoE3v1-05.gif) Now, let’s create the third collection – our **task collection**. We’ll follow our previous design to set up a simple task collection with the following fields: ``` Tasks collection: Task Name (task_name) Single-line Text Task Description (task_description) Multi-line Text ``` ### 3.3 Creating the Task collection 1. **Create Task collection**: - Click “Create collection” > Select **General collection** > Enter **Collection display name** (e.g., “Task collection”) and **Collection name** (e.g., “tasks”). - The **Collection name** is a unique ID, recommended to use English letters, numbers, or underscores for easier reference. - Submit to create. ![Create Task collection](https://static-docs.nocobase.com/Solution/demoE3v1-06.gif) 2. **Default Fields**: NocoBase generates default fields for each standard collection: - **ID**: A unique identifier for each record. - **Created at**: Automatically records when the task was created. - **Created By**: Automatically records the task creator. - **Last updated at** and **Last updated by**: Records each modification’s date and user. These default fields are exactly what we need, saving us from adding them manually. 3. **Create Custom Fields**: - **Task Name**: Click “Add Field” > Choose **Single line Text** > Set the 'Field display name' to “Task Name,” and the field name as “task_name.” ![Create Task Name Field](https://static-docs.nocobase.com/Solution/demoE3v1-07.gif) - **Task Description**: Add another field of **Long text** with the field name“task_description.” ![Create Task Description Field](https://static-docs.nocobase.com/Solution/demoE3v1-08.gif) Congratulations! The **task collection** is now defined, and you have successfully created your own task data structure. ### 3.4 Creating the Task Management Page With the task collection ready, let’s present it on a page using a table block. 1. **Create Task Management Page**: - Click “New Page,” name it “Task Management.” - Add a table block to display task data. ![Create Task Block](https://static-docs.nocobase.com/Solution/demoE3v1-10.gif) 2. **Add Data**: - “Hmm, why is there no data?” Don’t worry; let’s add some! - Click on “Configure Actions” at the top right, select **Add** action, and you’ll see an empty pop-up container. - Next, add a form block > select **Current Collection**. - Display the Task Name and Description fields, configure the submit action, and complete the form submission! ![Configure Action](https://static-docs.nocobase.com/Solution/demoE3v1-11.gif) 3. **Enter Data**: - Enter a test task, click submit, and success! Task data has been added. ![Submit Data](https://static-docs.nocobase.com/Solution/demoE3v1-12.gif) Exciting time! You successfully entered the first task data, wasn't that easy? ### 3.5 Task Filtering – Quickly Locate Tasks As tasks increase, how can you find specific ones quickly? Here’s where [**Filter Actions**](https://docs.nocobase.com/handbook/ui/actions/types/filter) come into play. In NocoBase, you can easily locate tasks by setting filter conditions. #### 3.5.1 Enabling Filter Actions First, we need to enable filter actions: - **Hover over “Configure actions”** and click the **Filter Switch**. ![Enable Filter](https://static-docs.nocobase.com/Solution/011734175681202414192812.png) #### 3.5.2 Using Filter Conditions Once enabled, the filter button appears on the page. Now, let’s test the filter by searching for tasks by **Task Name**: - In the filter panel, select Task Name, and enter your query. - Click “Submit” to see if the task list correctly displays the filtered results. ![Enable Filter](https://static-docs.nocobase.com/Solution/demoE3v1-13.gif) #### 3.5.3 Disabling Filter Actions If you no longer need the filter, simply toggle it off: - **Reset Filter Conditions** to ensure no active filters, then click “Reset.” - Toggle **Filter** off again to hide it. ![Disable Filter](https://static-docs.nocobase.com/Solution/demoE3v1-14.gif) ### 3.6 Task Editing and Deletion In addition to adding and viewing tasks, we also need to be able to [**Edit**](https://docs.nocobase.com/handbook/ui/actions/types/edit) and [**Delete**](https://docs.nocobase.com/handbook/ui/actions/types/delete) them. Since you’re familiar with adding blocks, fields, and actions, these will be straightforward: 1. **Edit Task**: - Add an **Edit** action in the task list configuration. Click Edit > Add Form (Edit) Block > Choose fields to edit. 2. **Delete Task**: - Similarly, enable the **Delete** action in the configuration, and the delete button appears. Click Delete > Confirm to remove the task from the list. ![Edit and Delete Task](https://static-docs.nocobase.com/Solution/demoE3v1-15.gif) Fantastic! You’ve now implemented **Create, Read, Update, and Delete (CRUD)** functionalities for tasks. ### Challenge Task As you become more proficient with NocoBase, here’s a little challenge: add a task status and allow attachments. Hints: - Add to your Task Collection: 1. **Status** field, a dropdown with options: **Not Started, In Progress, Pending Review, Completed, Cancelled, Archived**. 2. **Attachment** field. - Display the **Status** and **Attachment** fields in the Task Collection and the **Add** and **Edit** forms. Have any ideas yet? Don’t worry, the [next chapter (Chapter 4: Task and Comment Plugins)](https://www.nocobase.com/en/tutorials/task-tutorial-plugin-use) will reveal the answer. Let’s stay tuned! ### Continue Exploring Don’t hesitate to consult the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) if you have any questions! --- url: /tutorials/v1/04-plugins.md --- # Chapter 4: Task & Comment Plugins ## Review of the Last Section Remember the challenge from the last section? We configured **Status** and **Attachment** fields for our task collection and displayed them in the task list. Let’s reveal the solution! 1. **Status Field Configuration**: - Select the [Single select](https://docs.nocobase.com/handbook/data-modeling/collection-fields/choices/select) field and enter the following options: **Not Started, In Progress, Under Review, Completed, Canceled, Archived**. Feel free to choose colors you like to make the tasks more vibrant! ![Status Field Configuration](https://static-docs.nocobase.com/Solution/511734183711202414214112.png) 2. **Attachment Field Configuration**: - Create a new [**Attachment**](https://docs-cn.nocobase.com/handbook/file-manager/field-attachment) field, give it a name like “Attachment,” and hit submit. Simple as that! ![](https://static-docs.nocobase.com/Solution/251734183865202414214412.png) 3. **Display Creator and Status in Task List**: - In "configure columns", select the “Creator,” “Status,” and “Attachment” fields to show more critical information in the task list. ![Display Fields in Task List](https://static-docs.nocobase.com/Solution/201734183980202414214612.png) 4. **Display Fields in Add and Edit Forms**: - Don’t forget to select the Status and Attachment fields in the pop-up form, so you can easily see these fields when adding or editing a task. ![Display Fields in Forms](https://static-docs.nocobase.com/Solution/201734187340202414224212.png) Well done! Keep practicing these steps, and you’ll become more comfortable with the core features of NocoBase. Each step builds a solid foundation for future task management. Let’s keep going! --- ## 4.1 Task Content and Comments: Interactive Task Management So far, your task management system can handle basic task information. However, task management often requires more than a few lines of text; sometimes, we need richer content and real-time interaction between team members. ### 4.1.1 Markdown (Vditor): Making Task Content More Robust You may have noticed NocoBase offers both [**Rich Text**](https://docs.nocobase.com/handbook/data-modeling/collection-fields/media/rich-text) and [**Markdown**](https://docs.nocobase.com/handbook/data-modeling/collection-fields/media/markdown) editors, although these may not fully meet your needs. While the rich text editor is somewhat limited, the Markdown editor is easy to use but doesn’t support real-time preview. ![](https://static-docs.nocobase.com/Solution/311734187531202414224512.png)) Is there an editor that supports both real-time preview and extensive features? Absolutely! [**Markdown (Vditor)**](https://docs.nocobase.com/handbook/field-markdown-vditor) is the most powerful text editor in NocoBase, offering real-time preview, image uploading, and even voice recording. Plus, it’s built into the system and completely free! > **Markdown(Vditor)**: Used to store Markdown and render it using Vditor editor, supports common Markdown syntax such as list, code, quote, etc., and supports uploading images, recordings, etc.It also allows for instant rendering, where what you see is what you get. Let’s walk through enabling this powerful editor step-by-step. Remember our plugin manager? That’s where it’s found! 1. **Enabling the Markdown (Vditor) Plugin**: - Open the **Plugin Manager** in the upper right, search for "markdown" in the plugins, and enable the [**Markdown (Vditor)**](https://docs.nocobase.com/handbook/field-markdown-vditor). Don’t worry if the page briefly refreshes – it’ll be back in just a few seconds. ![Enable Markdown Plugin](https://static-docs.nocobase.com/Solution/181734187638202414224712.png) 2. **Creating a Markdown Field**: - Return to the task collection, click “Add Field,” and our Markdown Pro Plus is ready to go! ![](https://static-docs.nocobase.com/Solution/391734187779202414224912.png) - Give it a name, such as “Task Details (task_detail),” and enable all available features. 3. You may notice an option for “File collection.” Wondering if not selecting it affects file functionality? No worries; files will be stored in our default storage, so go ahead and use it. ![](https://static-docs.nocobase.com/Solution/281734187948202414225212.png) 4. **Testing the Markdown Field**: - Now, return to the task management page and start drafting your first Markdown text! Try pasting an image or uploading a file – it’s quite powerful, isn’t it? ![](https://static-docs.nocobase.com/Solution/551734188155202414225512.png) Your task collection is getting more feature-rich! With each step, your system’s capabilities expand. Next, let’s explore adjusting field arrangements for a cleaner interface. ### 4.1.2 Adjusting Field Layout As fields increase in the task collection, the page layout might appear cluttered. Not to worry, as NocoBase’s flexibility allows you to rearrange fields easily. **Adjusting Field Positions**: - Hover over the cross icon in the upper right corner of the field, then click and drag it to your preferred position. Release to complete the adjustment. Isn’t the page instantly tidier? ![Adjust Field Positions](https://static-docs.nocobase.com/Solution/demoE3v1-16.gif) This makes the layout more aligned with your preferences. Now, let’s add a comment function to the task collection to make team collaboration even easier. ## 4.2 Comment Function Having a task description is helpful, but sometimes team members need to add comments, discuss issues, and record feedback. Let’s start implementing this together. ### 4.2.1 Method One: Using the Comment Plugin #### 4.2.1.1 Installing the Comment Plugin > **Comments Plugin (Commercial Plugin):** Provides comment collection template and block to add commenting functionality for any collection. > > Note: When adding comments, be sure to link the target collection through a relationship field to prevent comment data conflicts. In the [**Plugin Manager**](https://docs.nocobase.com/handbook/plugin-manager), upload and enable the **Comments Plugin**. Once the plugin is enabled, a new “Comments Collection” option will appear in the data source. Click Add new (Add & update)> Upload Plugin > Drag in the zip file > Submit. After searching for comments, you’ll see the comment plugin! After enabling it, enter the data source and see the Comments Collection option – installation successful! ![Install Comment Plugin](https://static-docs.nocobase.com/Solution/demoE3v1-17.gif) ![View Comment Plugin](https://static-docs.nocobase.com/Solution/demoE3v1-18.gif) #### 4.2.1.2 Creating a Comments Collection Switch to the data source and create a new comments data collection called **Comments**. #### 4.2.1.3 Exploring the Relationship Between the Comments and Task Collection Now that we have a **Comments Collection**, you might wonder if we can simply add a comments section to the page. Not so fast – let’s think it through first: **Each task should have its own comments section**, and the relationship between comments and tasks should be **many-to-one**. So, how do we link comments to tasks? Exactly! We’ll use a [**relationship field**](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations) to create this connection in NocoBase. NocoBase allows us to use relationship fields to establish links between collections at the data level, like building bridges that tightly connect related data. ![](https://static-docs.nocobase.com/Solution/demoE3v1-19N.gif) **Why Choose a Many-to-One Relationship?** Why are we choosing a [**many-to-one**](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations/m2o) relationship instead of a [**one-to-many**](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations/o2m) or another type? Think about it: **Each task can have multiple comments**, so multiple comments can point to the same task. For this, we’ll create a **many-to-one** field in the comments collection that links to the tasks in the task collection. > A clever thought might have occurred to you: > Since the comments and tasks have a many-to-one relationship, can we also create a **one-to-many** field in the task collection that points to the comments collection? > **Exactly, well done!** One-to-many and many-to-one are reciprocal relationships, so we can create a one-to-many field in the task collection that links to the comments collection. You’re doing great! #### 4.2.1.4 Setting Up the Many-to-One Relationship Field Next, let’s create a many-to-one field in the comments collection to link it with the task collection. We can name this field **Associated Task (belong_task)**. During configuration, note these key settings: 1. **Source Collection**: From where is the relationship initiated? In this case, it’s the **Comments Collection**. 2. **Target Collection**: Which collection are we linking to? Here, it’s the **Task Collection**. > **Example for Foreign Keys and Target Collection Field Identification:** > Let’s clarify with an example: > > Imagine you’re handling a stack of high school transcripts, and your job is to link each transcript to the student it belongs to. Let’s say a transcript has the following information: > > - **Name**: Zhang San > - **Class**: Senior Class 35 > - **Exam ID**: 202300000001 > - **ID Number**: 111111111111 > > Now, let’s assume you want to use **Name** and **Class** to find Zhang San. However, since there might be many students named Zhang San in the same class, identifying them accurately can be challenging. > > **That’s where a unique identifier, such as the Exam ID, becomes useful**. Using the exam ID, you can precisely identify each student’s transcript. For instance, if you search with the Exam ID “202300000001,” the school will quickly respond: “This transcript belongs to Zhang San, the one with glasses in row three of Senior Class 35.” > > The same logic applies to setting up the **comments** relationship. A unique identifier from the task collection, like the **ID**, can be saved in the comments to link each comment to the corresponding task. > > This concept of **foreign keys** is the core of implementing many-to-one relationships – simple, right? In the comments collection, we save the unique **task_id** field from the task collection, binding comments to their associated tasks. #### 4.2.1.5 Handling Foreign Keys When Deleting Once we set up the many-to-one relationship in NocoBase, we also need to consider what happens to the comments if a task is deleted. You have several options: - **CASCADE**: When a task is deleted, all comments associated with it are also deleted. - **SET NULL** (default setting): When a task is deleted, the comment data is retained, but the foreign key field is set to null. - **RESTRICT and NO ACTION**: If a task has related comments, the system will prevent you from deleting it, ensuring no comments are lost. #### 4.2.1.6 Creating Reverse Relationships in the Task Collection Finally, select **Create Reverse Relationship Field in Target Collection** to make it easy to view all related comments from within a task. This improves data management efficiency. The location of relationship fields in NocoBase determines how data is accessed. To make the comments viewable directly from the task collection, we’ll create a **one-to-many** reverse relationship field in the task collection, linking it to the comments collection. When you reopen the task collection, you’ll see a comments field marked with a **one-to-many** relationship, allowing you to easily view and manage all related comments! ## 4.3 Page Setup ### 4.3.1 Enabling the Comments Collection Now comes an exciting moment: return to the edit popup, create a **comment block**, and check the needed features. Done! ![Enable Comments Collection](https://static-docs.nocobase.com/Solution/demoE3v1-20.gif) ### 4.3.2 Adjusting the Page Let’s style the page layout. Hover over the top-right of the edit button, choose a wider popup, and use what we’ve learned to drag the comment block to the right side of the popup. Perfect! ![Adjust Page Layout](https://static-docs.nocobase.com/Solution/demoE3v1-21.gif) Some of you may be wondering: “I want to set up comments too!” Don’t worry; I have another free option prepared for you. ### 4.2.2 Method Two: Creating a Custom Comments Collection If you haven’t purchased the comments plugin, we can still set up a similar comments feature by creating a custom collection. 1. **Create a New Comments collection**: - Create a **Comments Collection (comments2)**, add a **Comment Content (content)** field (Markdown type), and an **Associated Task (belong_task)** field (many-to-one type). ![](https://static-docs.nocobase.com/Solution/431734190783202414233912.png) 2. **Create a Comments List Block on the Page**: - In the edit popup for the task collection, add a [**List Block**](https://docs.nocobase.com/handbook/ui/blocks/data-blocks/list) (our third type of block, which can display field details), select comments, and test it: ![Create Comments List Block](https://static-docs.nocobase.com/Solution/demoE3v1-22N%20-1.gif) ![Create Comments List Block-2](https://static-docs.nocobase.com/Solution/demoE3v1-22N%20-2.gif) ## Summary Now you know how to enrich task content with Markdown (Vditor) and add a comments feature to tasks! Your task management system now has a complete functional foundation – you’re one step closer to creating a professional task management tool! Keep exploring and practicing; NocoBase has endless possibilities. If you run into any issues, don’t worry – I’ll be here to guide you through every step. In the [next chapter (Chapter 5: Tabs & Dynamic blocks)](https://www.nocobase.com/en/tutorials/task-tutorial-tabs-blocks), we’ll dive deeper into more block functions in NocoBase, helping you take your system to the next level. Keep up the great work! --- Explore freely and unleash your creativity! If you encounter any challenges, don’t hesitate to consult the [NocoBase Official Documentation](https://docs-cn.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions. --- url: /tutorials/v1/05-tabs-dynamic-blocks.md --- # Chapter 5: Tabs & Dynamic Blocks Hello, everyone! Welcome to Chapter 5! This chapter is packed with exciting content as we expand functionality in the task management page, enabling various viewing options. I bet you’ve been looking forward to this, right? No worries — I’ll guide you step-by-step, and as always, we’ll breeze through it together! ### 5.1 Tab Containers for Organizing Blocks We’ve already set up a task management page, but to make the system even more intuitive, we want tasks to be viewable in different modes, such as [**Table**](https://docs-cn.nocobase.com/handbook/ui/blocks/data-blocks/table), [**Kanban**](https://docs-cn.nocobase.com/handbook/block-kanban), [**Calendar**](https://docs.nocobase.com/handbook/calendar), and even [**Gantt Chart**](https://docs.nocobase.com/handbook/block-gantt). Using NocoBase’s tab feature, we can toggle between these different block layouts on the same page. Let’s walk through the steps. - **Creating a Tab** Let’s start by setting up a tab. 1. **Adding a Sub-tab** - Open your existing task management page, and within the page, create a sub-tab. You can name the first tab **“Table View”**, where we will display the task list block that was previously set up. 2. **Adding Another New Tab** - Next, add a second tab, naming it **“Kanban View”**. Here, we’ll create a Kanban block to manage tasks. ![Creating New Tab](https://static-docs.nocobase.com/Solution/demoE3v1-23.gif) Ready? Let’s dive into creating each block type! > **Block Introduction:** Blocks are data and content carriers that present information in the most suitable format on a website. Blocks can be added to a Page, Modal, or Drawer, and multiple blocks can be freely arranged via drag-and-drop. Using blocks in NocoBase makes page and feature building fast and flexible, while templates allow easy replication and referencing, greatly reducing setup time. ### 5.2 Kanban Block: Task Status at a Glance [**Kanban**](https://docs.nocobase.com/handbook/block-kanban) is an essential feature in task management systems, allowing intuitive task management through drag-and-drop. For instance, you can group tasks by status to get an instant overview of each task’s current stage. #### 5.2.1 Creating a Kanban Block 1. **Start Building a Kanban Block** - Within the **Kanban View** tab, click “Create Block,” select the task collection, and an option will appear asking which field you want to use for task grouping. 2. **Choosing a Grouping Field** - We’ll select the **Status** field to group tasks by their status. (Grouping fields should be of type “Dropdown (single select)” or “Radio group.”) 3. **Adding a Sorting Field** - Within the Kanban view, cards can be organized by a sorting field. To set this up, create a new sorting field named **Status Sort (status_sort)**. - This field allows cards to be arranged vertically within each status group. Later, when we drag and drop cards, the sorting values will be updated automatically and can be reviewed in the form. ![Creating a Kanban Block](https://static-docs.nocobase.com/Solution/demoE3v1-24.gif) #### 5.2.2 Selecting Fields and Configuring Operations - Lastly, be sure to select fields in the Kanban block, like task name and task status, to ensure that each card contains complete, relevant information. ![Kanban Field Display](https://static-docs.nocobase.com/Solution/demoE3v1-25.gif) ### 5.3 Using Templates: Copying and Referencing After creating the Kanban block, we’ll need to set up an **Add Form**. Here, NocoBase provides a very useful feature — you can [**Copy or Reference**](https://docs.nocobase.com/handbook/ui/blocks/block-templates#%E5%A4%8D%E5%88%B6%E5%92%8C%E5%BC%95%E7%94%A8%E7%9A%84%E5%8C%BA%E5%88%AB) a form template created previously, sparing you from reconfiguring a new one each time. #### 5.3.1 **Saving a Form as a Template** - In your existing Add Form, hover over the form settings and select “Save as Template.” You might name this template “Task Form_Add.” ![Saving Form as Template](https://static-docs.nocobase.com/Solution/demoE3v1-26.gif) #### 5.3.2 **Copying or Referencing a Template** When creating a form in the Kanban view, you’ll see two options: “**Copy Template**” and “**Reference Template**.” You might wonder, what’s the difference? - [**Copy Template**](https://docs.nocobase.com/handbook/ui/blocks/block-templates#%E5%A4%8D%E5%88%B6%E5%92%8C%E5%BC%95%E7%94%A8%E7%9A%84%E5%8C%BA%E5%88%AB): This creates a duplicate of the form, allowing you to make independent changes without affecting the original form. - [**Reference Template**](https://docs.nocobase.com/handbook/ui/blocks/block-templates#%E5%A4%8D%E5%88%B6%E5%92%8C%E5%BC%95%E7%94%A8%E7%9A%84%E5%8C%BA%E5%88%AB): This “borrows” the original form, so any changes made to it will automatically update all other forms that reference it. ![Copy and Reference Template](https://static-docs.nocobase.com/Solution/demoE3v1-27.gif) --- ### 5.4 Calendar Block: Clear Task Scheduling Next, let’s create a [**Calendar Block**](https://docs.nocobase.com/handbook/calendar) to help manage task schedules more effectively. #### 5.4.1 Creating a Calendar View ##### 5.4.1.1 **Adding Date Fields** To use the calendar view, the task collection needs **Start Date** and **End Date** fields: - **Start Date (start_date)**: Marks the start date of the task. - **End Date (end_date)**: Marks the task’s end date. ![Add Date Fields](https://static-docs.nocobase.com/Solution/391734209379202415044912.png) #### 5.4.2 Creating the Calendar Block In the Calendar View, create a calendar block, select the task collection, and set it up using the **Start Date** and **End Date** fields. This configuration will display tasks as spans on the calendar, offering a clear view of task timelines. ![Building Calendar View](https://static-docs.nocobase.com/Solution/demoE3v1-28.gif) ![Building Calendar View](https://static-docs.nocobase.com/Solution/demoE3v1-29N.gif) #### 5.4.3 Exploring Calendar Interactions On the calendar, you can easily choose date, click to edit task details, and remember to copy or reference templates as needed. ![Calendar Operations](https://static-docs.nocobase.com/Solution/demoE3v1-30.gif) ### 5.5 Gantt Block: The Ultimate Tool for Task Management The last block we’ll explore is the [**Gantt Block**](https://docs.nocobase.com/handbook/block-gantt), a tool widely used in project management to track task progress and dependencies. #### 5.5.1 Creating a “Gantt View” Tab #### 5.5.2 **Adding a “Completion Percentage” Field** To show task progress effectively in the Gantt chart, we’ll need to add a new field named **Completion Percentage (complete_percent)**, with a default value of 0%. ![Add Completion Percentage Field](https://static-docs.nocobase.com/Solution/061734211206202415052012.png) #### 5.5.3 **Creating the Gantt Block** In the Gantt View, create a Gantt block, select the task collection, and configure it with the start date, end date, and completion percentage fields. ![Building Gantt View](https://static-docs.nocobase.com/Solution/demoE3v1-31.gif) #### 5.5.4 **Using the Gantt Dragging Feature** In the Gantt view, you can adjust task progress and timelines by dragging, with the start date, end date, and completion percentage fields updating automatically as you make changes. ![Gantt Dragging](https://static-docs.nocobase.com/Solution/demoE3v1-32.gif) ### Summary Fantastic! You now know how to use various blocks in NocoBase to display task data, including [**Kanban Block**](https://docs.nocobase.com/handbook/block-kanban), [**Calendar Block**](https://docs.nocobase.com/handbook/calendar), and [**Gantt Block**](https://docs.nocobase.com/handbook/block-gantt). These blocks not only make task management more intuitive but also provide great flexibility. But this is just the beginning! Imagine a team where different members have distinct roles — how can we ensure seamless collaboration and data security, allowing each person to see and edit only the content relevant to them? Are you ready? Let’s move on to the next chapter: [Chapter 6: User & Permissions](https://www.nocobase.com/en/tutorials/task-tutorial-user-permissions). Keep exploring and unleash your creativity! If you encounter any issues, don’t forget to consult the [NocoBase Official Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions. --- url: /tutorials/v1/06-user-permissions.md --- # Chapter 6: User & Permissions In team collaboration, it’s essential for everyone to clearly understand their roles and permissions to ensure smooth progress. Today, we will learn about role creation and permission management to make teamwork more streamlined and organized. Don’t worry—this process is simple. We’ll guide you through it step by step, providing guidance at each critical stage. If you encounter any issues, please feel free to reach out to our official forum for assistance. ### Requirements Analysis We need a "Partner" role with specific permissions to participate in task management but without the ability to freely modify others' tasks. This approach allows us to assign tasks and collaborate flexibly. ![Role Structure Diagram](https://static-docs.nocobase.com/Solution/WSy8bi0zOozbBKxzlimcyje4n7f.png) > **Introduction to Roles and Permissions:** Roles and permissions are essential mechanisms for managing user access and operations. Each user can have multiple roles. > > By setting role permissions, we can control user behavior and operations within the system, as well as limit displayed functionality, which is crucial for access control. > Using roles and permissions to bind users enables you to manage your system more effectively. As an administrator, you can assign and adjust permissions as needed. ### 6.1 **Role Creation and Assignment** #### 6.1.1 **Creating the “Partner” Role** - Click on ["**Users and Permissions**"](https://docs.nocobase.com/handbook/users) in the top right of the interface, and select ["**Roles and Permissions**"](https://docs.nocobase.com/handbook/acl). This is where we configure roles and manage permissions. - Click on the "**Create Role**" button, and a dialog box will appear. Here, name the role **Partner** and confirm to save. ![](https://static-docs.nocobase.com/Solution/demoE3v1-33.gif) You have successfully created a new role! Next, we need to assign permissions to this role to ensure they can participate in task management. #### 6.1.2 **Assigning the New Role to Yourself** To ensure that the role permissions are working as expected, we can first assign this role to our own account for testing. It’s straightforward: - In the user management section, find your account, click to enter, select "**Roles & Permissions**," and choose "**Partner**." ![](https://static-docs.nocobase.com/Solution/demoE3v1-34.gif) Now you can use your account to simulate the "Partner" role experience. Next, let’s see how to switch roles. #### 6.1.3 **Switching to the “Partner” Role** Since you’ve assigned yourself the "Partner" role, let's explore how to switch roles. - Click on **Personal Center** in the upper right corner, then choose "**Switch Role**." - You may notice that the "Partner" role does not immediately appear in the list. Don’t worry; just **refresh the page/clear cache**, and the role should appear. ![](https://static-docs.nocobase.com/Solution/demoE3v1-35.gif) ### 6.2 Assigning Page Permissions to the Role Once you switch to the "Partner" role, you may notice that there are no visible pages or menus. This is because we haven’t assigned permissions for the role to access specific pages yet. No problem—let’s configure access permissions for the "Partner" role. #### 6.2.1 **Assigning Task Page Permissions to the “Partner” Role** - First, switch back to the **Root** role (Super Administrator), and go to the "**Roles &Permissions**" page. - Click on the "Partner" role to open the configuration page. Here, you will see a "**Menu**" tab, which represents all pages in the system. - Check the "**Task Management**" page permission to allow the "Partner" role to access the task management page. Return to **Personal Center**, switch back to the "Partner" role, and you should now be able to see the task management menu page. ![](https://static-docs.nocobase.com/Solution/demoE3v1-36.gif) #### 6.2.2 Setting Collection Action and Operation Permissions Although the "Partner" role now has access to the task management page, we need to further restrict their operational permissions. We want the "Partner" to: - **View and edit** tasks assigned to them, - **Update task progress**, - But **not create or delete tasks**. To achieve this, we need to configure the permissions for the "Tasks." Let’s proceed! ##### 6.2.2.1 **Configuring Collection Action Permissions for the “Partner” Role** - Go to the "**Roles and Permissions**" page, click on the "Partner" role, and switch to the "**Data Source**" tab. - Here, you will find settings for "Action permissions." Locate the "**Tasks**," and assign the "Partner" the “View” and “Edit” permissions. - We won’t assign "Add" or "Delete" permissions to other roles, so no need to assign these from the start. ![](https://static-docs.nocobase.com/Solution/demoE3v1-37.gif) At this point, the Partner role has the permissions to view and edit the task data. Next, we need further controls to ensure they can only edit tasks assigned to them. ### 6.3 Adding a “Responsible Person” Field to Tasks Now, we’ll assign a responsible person to each task. By designating a responsible person, we ensure that only the task’s responsible person can modify the task, while others can only view it. To achieve this, we’ll use a **relationship field** to link the task collection with the user collection. #### 6.3.1 **Creating the “Responsible Person” Field** 1. Go to the "**Task collection**," click on "**Add Field**," and select "**Relationship Field**." 2. Choose a "**Many-to-One**" relationship (since each task can have only one responsible person, but a user can be responsible for multiple tasks). 3. Name the field “**Responsible Person (Assignee)**” . No need to select the reverse relationship for now. ![](https://static-docs.nocobase.com/Solution/511734242031202415135312.png) #### 6.3.2 **Displaying the “Responsible Person” Field** Next, we need to ensure the "Responsible Person" field is displayed in the task management page’s collection and form, making it easy to assign a responsible person to each task. (If the default display field is set to an ID, simply change the title field to “Nickname” to display names.) ![](https://static-docs.nocobase.com/Solution/181734242358202415135912.png) ### 6.4 Using **User & Permissions Management** to Control Access Here comes the highlight! Now, we’ll use NocoBase’s [**Permission Management**](https://docs-cn.nocobase.com/handbook/acl) to implement a powerful feature: **only the responsible person and creator of a task can edit it**, while others can only view it. This is where NocoBase’s flexibility truly shines. #### 6.4.1 **Simple Trial — Only the Responsible Person Can Edit the Form** We want only the task’s responsible person to be able to edit it, so we’ll set the following conditions: - Return to the “Partner” role’s data collection permissions, open the configuration for the task collection, and click on “Data Scope” under “Edit Permission.” - Create a custom rule called “Assignee Can Edit”: **When "Assignee/ID" equals "Current User/ID,"** editing is permitted; This means that only the task’s responsible person can edit it, while others can only view it. - Since we’re using the user collection for the responsible person field, and the logged-in user is also in the user collection, this rule perfectly meets our initial requirement. Click Add, then Confirm. ![](https://static-docs.nocobase.com/Solution/demoE3v1-38.gif) Now let’s go back to the page to check: Perfect! When we switch to the Partner role and revisit the page, only tasks for which we are the assignee will can edit tasks correctly. ![](https://static-docs.nocobase.com/Solution/demoE3v1-39N.gif) #### 6.4.2 **Adding Conditions for Creators to Modify Forms** You may have noticed a new issue: Since we are not the responsible person for most tasks, we can’t edit the form ourselves, and other colleagues also cannot see task details! No worries—remember, we assigned the Partner role **view permissions for all data** earlier? - Return to the page configuration, select “View” and add a view operation. ![](https://static-docs.nocobase.com/Solution/081734249068202415155112.png) - Similar to the edit permissions dialog, create a view dialog, and make sure to choose the “Details” section. ![](https://static-docs.nocobase.com/Solution/181734249138202415155212.png) ![](https://static-docs.nocobase.com/Solution/081734249308202415155512.png) Done! ### 6.5 **Verifying Permission Control** If you switch between different users to view the form, you’ll notice that the form sections automatically adjust to show only the operations permitted for each user. All tasks for which a user is responsible will display editing options, while tasks outside their responsibility will only show viewing options. Switching back to the Root role restores full permissions. This is the powerful permission control system of NocoBase! Next, you can freely assign task responsibilities, invite teammates to collaborate, and test the permissions we have set. #### 6.5.1 **Creating a New User and Assigning a Role** - Create a new user, for example, **Tom**, and assign him the "**Partner**" role. ![](https://static-docs.nocobase.com/Solution/571734249357202415155512.png) ![](https://static-docs.nocobase.com/Solution/221734249442202415155712.png) - In the task management page, assign a few tasks to **Tom**. #### 6.5.2 **Login Testing** Have Tom log into the system to check if he can view and edit tasks assigned to him. Based on the permission rules, Tom should only be able to edit tasks he is responsible for, while all other tasks are read-only. The edit form permissions have now been successfully synchronized across all pages! ![](https://static-docs.nocobase.com/Solution/demoE3v1-40N.gif) ### Summary Congratulations! By now, you’ve learned how to create roles, assign permissions, and set custom permissions in NocoBase, ensuring that team members can only edit tasks assigned to them. Through these steps, you have established a clear, organized permission management system for team collaboration. ### Challenge Task Currently, Tom can view and edit tasks assigned to him, but you may notice he **cannot leave comments** or engage in task discussions. So, how can we assign Tom permissions to freely comment and participate in discussions? This will be an interesting challenge! **Challenge Hint:** You can revisit the role permission settings and adjust the "Partner" role, especially the collection settings, to see how to allow Tom commenting privileges while ensuring his other task restrictions remain intact. Give it a try! We’ll reveal the solution in the following section. In the next chapter, we’ll implement the “Team Member Activity” feature and introduce another powerful module: [**Workflow**](https://docs.nocobase.com/handbook/workflow). With Workflow, you can enable data flow, trigger various actions, and automate complex business processes in your system. Ready to continue exploring? See you in [Chapter 7: Workflow](https://www.nocobase.com/en/blog/task-tutorial-workflow) --- Explore further and unleash your creativity! If you encounter any issues, don’t forget to check the [NocoBase Official Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions. --- url: /tutorials/v1/07-workflow.md --- # Chapter 7: Workflow Congratulations on reaching the final chapter! Here, we’ll introduce and briefly explore the powerful workflow features in **NocoBase**. This feature lets you automate tasks within the system, saving time and enhancing efficiency. ### Challenge Solution from the Previous Chapter Before diving in, let’s quickly recap the solution to the last challenge. We successfully set up **comment permissions** for the "Partner" role as follows: 1. **Add Permission** : Allows users to post comments. 2. **View Permission** : Allows users to view all comments. 3. **Edit Permission** : Users can edit only their own comments. 4. **Delete Permission** : Users can delete only their own comments. ![](https://static-docs.nocobase.com/Solution/221734250582202415161612.png) With these permissions, Tom can freely post comments, view others’ comments, and ensure that only he can edit or delete his own posts. ![](https://static-docs.nocobase.com/Solution/241734250764202415161912.png) --- Now, let’s implement an automation: **whenever a task assignee is changed, the system will automatically send a notification to the new assignee, informing them of the task transfer**. > **Workflow**: The Workflow Plugin is a powerful automation tool commonly used in Business Process Management (BPM) to design and configure business processes based on data models. > > By defining triggers and configuring process nodes, this plugin automates the flow of business processes, enabling efficient task management and data-driven operations. ### 7.1 Creating a Workflow #### 7.1.1 Setting Up a Workflow in the Admin Page First, switch to the **Root Role** – the system administrator role with all permissions. Then, navigate to the [**Workflow Module**](https://docs.nocobase.com/handbook/workflow). ![](https://static-docs.nocobase.com/Solution/001734250860202415162112.png) Click the **“Add”** button in the top right corner to create a new workflow, and fill in the basic information: - **Name**: Send a system notification when the assignee is changed. - **Trigger Type**: Select "Collection Event." ![](https://static-docs.nocobase.com/Solution/151734250935202415162212.png) #### 7.1.2 Explanation of Trigger Types 1. [**Collection Event**](https://docs.nocobase.com/handbook/workflow/triggers/collection): Triggers when changes occur within a collection (add, modify, delete). This is ideal for tracking updates to task fields, such as when the assignee changes. 2. [**Scheduled Task**](https://docs.nocobase.com/handbook/workflow/triggers/schedule): Automatically triggers at specified times, suitable for schedule-based automation. 3. [**Post-action Event**](https://docs.nocobase.com/handbook/workflow/triggers/post-action): Binds to an action button and triggers after a user action, like clicking "save" on a form. In future cases, you may also find trigger types such as "Pre-action Event," "Custom Action Event," and "Approval," which can be unlocked through additional plugins. In this scenario, we use the [**Collection Event**](https://docs.nocobase.com/handbook/workflow/triggers/collection) to monitor changes to the "Task Assignee" field in the "Task" collection. After submitting the workflow, click **Configure** to enter the workflow settings page. ![demov3N-37.gif](https://static-docs.nocobase.com/Solution/demoE3v1-41.gif) --- ### 7.2 Configuring Workflow Nodes #### 7.2.1 Setting Trigger Conditions Let’s begin building the automated notification process by configuring the first node and setting conditions to automatically activate the workflow under specific circumstances. - **Collection**: Select "Task." (This collection triggers the workflow and pulls relevant data. We want the workflow to activate when the "Task" collection is updated.) - **Trigger on**: Select "After adding or updating data." - **Trigger Field**: Choose "Task Assignee." - **Only triggers when match conditions**: Select "Task Assignee / ID" "exists," ensuring a notification is only sent when a task is assigned. - **Preload associations**: Choose "Task Assignee," enabling its information to be used in the following steps. ![](https://static-docs.nocobase.com/Solution/demoE3v1-42.gif) --- #### 7.2.2 Enabling the "In-app Message" Channel Next, we’ll create a node to send notifications. Before proceeding, we need to create an [“In-app Message” channel](https://docs.nocobase.com/handbook/notification-in-app-message) for notifications. - Go back to Plugin Management, select "Notification Management," and create a task notification (task_message). - After creating the channel, return to the workflow and add a “Notification” node. ![](https://static-docs.nocobase.com/Solution/demoE3v1-43.gif) - **Node Configuration**: - **Channel**: Select "Task Notification." - **Receivers**: Choose “Trigger Variable / Trigger Data /Task Assignee / ID” to target the new assignee. - **Message Title**: Enter “Assignee Change Reminder.” - **Message Content**: Enter “You have been assigned as the new task assignee.” Once complete, click the toggle in the upper right to activate this workflow. ![](https://static-docs.nocobase.com/Solution/demoE3v1-44N%20-1.gif) ![](https://static-docs.nocobase.com/Solution/demoE3v1-44N%20-2.gif) ![](https://static-docs.nocobase.com/Solution/551734252475202415164712.png) Configuration complete! #### 7.2.3 Testing the Notification It’s an exciting moment. Go back to the page, edit any task, change the assignee, and click submit. The system has now sent the notification! ![](https://static-docs.nocobase.com/Solution/001734252600202415165012.png) ![](https://static-docs.nocobase.com/Solution/141734252674202415165112.png) --- That’s the basic workflow setup. However, there’s one more improvement to make: The notification should dynamically insert task information so users know which task was reassigned. ### 7.3 Refining the Workflow #### 7.3.1 Version Management Return to the workflow configuration. You’ll notice the workflow interface is now grayed out and cannot be edited. No worries. Click the ellipsis in the upper right corner > [**Duplicate to New Version**](https://docs.nocobase.com/handbook/workflow/advanced/revisions), and you’ll enter the new version’s configuration page. The previous version is retained, so by clicking the **Version** button, you can switch back to historical versions (note: executed workflow versions cannot be modified). ![](https://static-docs.nocobase.com/Solution/demoE3v1-45.gif) #### 7.3.2 Enhancing Notification Content Now, let’s personalize the notification by adding details about the task transfer. - **Edit the Notification Node**. Change the message content to: “Task 《【Task Name】》 has been reassigned to: 【Task Assignee / Nickname】.” - Use the variable panel on the right to add the task name and Task Assignee fields. - Then, click the toggle in the upper right to activate this version. ![](https://static-docs.nocobase.com/Solution/demoE3v1-46.gif) With the updated workflow version activated, you’ll see the task name in the system notification upon the next test. ![](https://static-docs.nocobase.com/Solution/demoE3v1-47.gif) --- ### Summary Fantastic! You’ve successfully created an automated workflow that triggers based on changes to task ownership. This feature not only saves time but also enhances team collaboration. At this point, our task management system has gained powerful capabilities. --- ### Conclusion and Next Steps You’ve now completed a fully functional task management system from scratch – covering task creation, comments, role permissions, workflows, and system notifications. The flexibility and extensibility of NocoBase open up limitless possibilities for you. In the future, you can explore more plugins, customize features, or create complex business logic. With this knowledge, you’ve mastered the basics and core concepts of NocoBase. We look forward to seeing your next innovation! For any questions, feel free to consult the [NocoBase official documentation](https://docs.nocobase.com/) or join the [NocoBase community](https://forum.nocobase.com/) for discussions. Keep exploring and unleash endless potential! --- url: /tutorials/v1/08-project-overview.md --- # Project Management Overview ## Introduction Dear reader, congratulations on creating a foundational task management system! But this is only the beginning. Just like building a house, the groundwork is laid, and now we begin constructing each layer, elevating the system to be even more comprehensive and powerful. ## Achievements So Far - Developed essential task data management features. - Enabled multiple views for task data presentation ([Table](https://docs.nocobase.com/handbook/ui/blocks/data-blocks/table), [Kanban](https://docs.nocobase.com/handbook/block-kanban), [Calendar](https://docs.nocobase.com/handbook/calendar), [Gantt charts](https://docs.nocobase.com/handbook/block-gantt) - Configured [Notifications](https://docs-cn.nocobase.com/handbook/notification-in-app-message) for task ownership changes. ![](https://static-docs.nocobase.com/project-management-en.drawio.svg) ## Future Development Vision ### 1. Knowledge Base Picture a hub where your team’s documents, expertise, and insights converge. How can we effectively organize and retrieve this knowledge? We’re designing a multi-functional Knowledge Center: - Hierarchical document structure for organized, intuitive browsing [(Tree Collection)](https://docs.nocobase.com/handbook/collection-tree). - Support for diverse document types: technical guides, team wiki, announcements. - Robust access controls for secure information sharing (permissions, status, rules). ### 2. Personalized Dashboard Everyone needs a workspace that reflects their unique priorities: - Task Focus Area: Highlight crucial, time-sensitive tasks (filtered view). - Visual Progress Tracking: Clear data charts that bring work progress to life [(Chart)](https://docs.nocobase.com/handbook/data-visualization/user/chart-block). - Customized Information Display: Tailor the dashboard to display what matters most to you (document library/announcements). ### 3. Subtask Management Some tasks are like an elephant that needs to be "eaten" bit by bit: - Task Breakdown: Split large tasks into manageable subtasks. - Progress Tracking: Easily monitor both the big picture and detailed progress. - Multi-Level Organization: Support for nested subtasks with a clear hierarchy. ### 4. Time Tracking and Progress Reporting (Optional) See project progress in a single glance: - Daily time logs. - Progress updates and status tracking. - Work hours statistics and analytics. - Visualized progress displays. ### 5. Meeting Room Booking Management(Optional) Make meetings productive rather than a burden: - Scheduling and managing meeting agendas. - Room reservations. - ... ### 6. Towards a Complete Project Management Platform Our ultimate objective is to unify all these features into a robust project management platform that meets the evolving needs of any team. As we advance, future additions might include: - Cost Analysis. - Project Performance Analytics. - … ## Conclusion In the following sections, we’ll bring each of these features to life, one by one. With each step, your system will grow stronger and more capable. Remember, Rome wasn’t built in a day—we’re taking it step by step, ensuring each component integrates smoothly and works flawlessly. Are you ready to embark on this exciting journey of transformation? Let’s press on! [**Next Chapter:**](https://www.nocobase.com/en/tutorials/project-tutorial-knowledge-base) We’ll begin by developing the Knowledge Base creating a structured and intuitive document management system. --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions and support. --- url: /tutorials/v1/09-knowledge-base.md --- # Chapter 8: Knowledge Base - Tree Collection ### 8.1 Welcome to a New Chapter In this chapter, we embark on creating a knowledge base—a comprehensive module designed to help us manage and organize documents, tasks, and information seamlessly. By designing and implementing a tree-structured document collection, we’ll establish an efficient system for tracking document status, managing attachments, and linking related tasks. ### 8.2 Exploring Database Design #### 8.2.1 Initial Design and Creating the Document Collection We’ll begin with a straightforward database design by building a "Document Collection" to catalog all document information. Key fields in the Document Collection include: * **Title**: Single line text. * **Content**: Markdown(Vditor). * **Document Status**: Single select, with options including Draft, Published, Archived, and Deleted. * **Attachment**: Attachment, Allows adding files and images to enrich document content. * **Related Task**: A many-to-one relationship field to link the document with a task for easy reference in task management. ![](https://static-docs.nocobase.com/Solution/331734255873202415174412.png) As our system evolves, we’ll continue to add fields for more detailed document management. #### 8.2.2 Constructing a Tree Structure for Directory Management > A tree collection (provided by the plugin [plugin-collection-tree](https://docs.nocobase.com/handbook/collection-tree)), is a collection structure mirrors a tree, where each data item may have one or more child items, and those child items can, in turn, have their own descendants. To ensure organization and hierarchy, we’ll structure our Document Collection as a [**Tree collection**](https://docs.nocobase.com/handbook/collection-tree), allowing for intuitive parent-child classification. This setup automatically generates the following fields: ![](https://static-docs.nocobase.com/Solution/381734255938202415174512.png) - **Parent ID**: Identifies the parent document for the current document. - **Parent**: A many-to-one field establishing parent-child relationships. - **Children**: A one-to-many field that enables viewing all child documents under a parent document. ![](https://static-docs.nocobase.com/Solution/581734256018202415174612.png) These fields are essential to maintaining the directory hierarchy, so it’s recommended not to alter them. We’ll also establish an association with the Task Collection [(Many-to-One)](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations/m2o), complete with inverse field, so that document lists can be directly created within the task association popup. ### 8.3 Creating the Document Management Page #### 8.3.1 Adding a New Document Management Menu In the main system menu, add a new page—"Document Management", then select an appropriate icon. Next, create a table block for our Document Collection, enabling basic actions like Add, Delete, Edit, and Search, and input test data to validate the design of the data collection. ![](https://static-docs.nocobase.com/Solution/111734257351202415180912.png) #### Exercise 1. Add a parent document named “Document 1” on the Document Management page. 2. Under “Document 1,” add a child document named “Chapter 1.” #### 8.3.2 Converting to a Tree Table View I know you're probably wondering why it's not a catalog tree structure. By default, the table block displays as a regular table. Here’s how to convert it to a tree table view: 1. Click on the top right corner of the table block > Tree Table. You will notice that the moment you select it, a "Expand All" toggle appears below the Tree Table. At the same time, the previously created "Chapter One" disappears. 2. Click on the "Expand All" option below the Tree Table to activate it. Now, we can see the parent-child structure of the document displayed more intuitively, allowing us to easily view and expand all document levels. Let's proceed with the "Add Sub-record" action. Tree table conversion complete! ![](https://static-docs.nocobase.com/Solution/291734257429202415181012.png) ![](https://static-docs.nocobase.com/Solution/321734257492202415201112.png) #### 8.3.3 Configuring “Add Child Record” To add child records, we’ll configure the necessary elements. When the Parent Record field is selected, it defaults to "read-only" as new entries are created within the current document directory. ![](https://static-docs.nocobase.com/Solution/351734262415202415193312.png) If there is too much task data, you might find it particularly troublesome to assign related tasks. We can set a default value for task filtering, making it equal to the tasks associated with the parent record. ![](https://static-docs.nocobase.com/Solution/171734262577202415193612.png) ![](https://static-docs.nocobase.com/Solution/211734262761202415193912.png) The default value may not take effect immediately, let's close and click again to see that it has been auto-filled~! ### 8.4 Configuring Form Templates and Task Associations #### 8.4.1 Creating Table and Form [Templates](https://docs.nocobase.com/handbook/block-template) To ease future management, save the Document Table along with its create/edit forms as [templates](https://docs.nocobase.com/handbook/block-template) for reuse on other pages. ![](https://static-docs.nocobase.com/Solution/571734263097202415194412.png) #### 8.4.2 Displaying a Copied Document Table In the Task view popup, [add a new tab](https://docs-cn.nocobase.com/manual/ui/pages#%E6%A0%87%E7%AD%BE%E9%A1%B5) titled "Documents." Within this tab, add a form block > Other Records > Documents > “Copy Template” > and import the previously created document table template. (Ensure you select [**Copy Template**](https://docs.nocobase.com/handbook/block-template)). ![](https://static-docs.nocobase.com/Solution/111734263351202415194912.png) This method streamlines document list creation. #### 8.4.3 Adjusting Task Associations Since we copied an external table template, it’s not yet linked with the Task Collection. You might notice it displays all document data, which isn’t ideal. This situation is quite common. If we have not created a corresponding relationship field but still need to display related data, we must manually link the two. (**Note:** We use a [copy of the template](https://docs.nocobase.com/handbook/block-template), not a [reference template](https://docs.nocobase.com/handbook/block-template); otherwise, all changes we make will synchronize with other tables block!) - Data Display Association We click on the top right corner of the table block and [Set the data scope"](https://docs.nocobase.com/handbook/ui/blocks/block-settings/data-scope) to: 【Task/ID】= 【Current Popup Record/ID】 ![](https://static-docs.nocobase.com/Solution/581734263458202415195012.png) ![](https://static-docs.nocobase.com/Solution/291734263669202415195412.png) Successfully, the documents retained within the table are those associated with our task. - Add form block association. Enter the Add Block screen: For fields related to associated tasks, set the [default value](https://docs.nocobase.com/handbook/ui/fields/field-settings/default-value) to > [Parent Popup Record]. The parent popup is part of the “View” operation for the current task data and will directly link to the corresponding task data. Setting it to [read-only (view mode)](https://docs.nocobase.com/handbook/ui/fields/field-settings/pattern)indicates that only the current task can be linked within this popup. ![](https://static-docs.nocobase.com/Solution/051734264005202415200012.png) ![](https://static-docs.nocobase.com/Solution/571734264117202415200112.png) Done! Now, newly added and displayed items will all be associated with the document of the current task. If you're attentive, you might add the association filter in “Edit” and “Add Subtask.” To make the tree structure clearer and the Actions Column neater, let's move the title to the first column. ![](https://static-docs.nocobase.com/Solution/451734264225202415200312.png) ### 8.5 Filtering and Searching in Document Management ##### 8.5.1 Adding a [Filter Block](https://docs.nocobase.com/handbook/ui/blocks/filter-blocks/form) Add a filter block to Documents to enable advanced search options. - Add a filter block on the Document Management page. - Select the form for filtering and drag it to the top. - Check fields such as Title, Status, and Task as filter criteria. - Add “Filter” and “Reset” actions. This filter form acts as a search box, allowing for rapid document retrieval with keyword entry. ![](https://static-docs.nocobase.com/Solution/571734264297202415200412.png) ![](https://static-docs.nocobase.com/Solution/111734264491202415200812.png) #### 8.5.2 [Connecting Data Blocks](https://docs.nocobase.com/handbook/ui/blocks/block-settings/connect-block) At this point, you may notice that clicking yields no result. We need one final step: linking blocks with search functionality to each other. - Click on **Settings** in the upper right corner of the block and select [**Connecting Data Blocks**](https://docs.nocobase.com/handbook/ui/blocks/block-settings/connect-block). ``` Here, you’ll see a list of available blocks that can be linked. Since we created a document form, it will search for all data blocks related to the document table (there’s only one on this page) and display them as options. No need to worry about getting confused, as moving the mouse over an option will automatically focus the screen on the corresponding block. ``` - Click to enable the block you want to link and test the search. ![](https://static-docs.nocobase.com/Solution/demoE3v1-48.gif) Click the configuration button in the top-right corner of the filter block to link it to the main data block of the document table. This way, whenever you set a condition in the filter block, the table block automatically updates the results based on the condition. ### 8.6 [Setting Permissions](https://docs.nocobase.com/handbook/acl) for the Knowledge Base To protect documents and standardize management, assign permissions based on user roles, allowing different users to view, edit, or delete documents according to their [permissions](https://docs.nocobase.com/handbook/acl). However, we will be upgrading the document database to add features for news and task announcements, allowing for more flexible permissions. ### 8.7 Summary and Next Steps In this chapter, we built the foundation of a knowledge base, incorporating a Document Collection, [Tree Collection](https://docs.nocobase.com/handbook/collection-tree), and task associations. With added filter blocks and reusable templates, we’ve optimized document management for efficiency. [Next](https://www.nocobase.com/en/tutorials/project-tutorial-task-dashboard-part-1), we’ll build a personal dashboard featuring data analysis [charts](https://docs.nocobase.com/handbook/data-visualization) and key information displays! --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions and support.s --- url: /tutorials/v1/10-dashboard-charts.md --- # Chapter 9: Task Dashboard & Charts Dear friends, We’ve finally reached the long-awaited chapter on visualization! In this chapter, we’ll explore how to quickly zero in on the information we truly need amidst the clutter. As managers, we mustn’t lose our way in the complexity of tasks! Let’s tackle task statistics and information display together with ease. ### 9.1 Focus on Key Information Our goal is to gain a quick overview of team tasks, highlighting those we are responsible for or interested in, without being overwhelmed by unnecessary data. ![](https://static-docs.nocobase.com/20241208170708.png) Let’s start by creating a team task statistics [chart](https://docs.nocobase.com/handbook/data-visualization/user/chart-block). #### 9.1.1 Create a [Chart Block](https://docs.nocobase.com/handbook/data-visualization/user/chart-block) With a new page: 1. **Create a New Chart Data Block**. (Within this main block, you can build multiple charts.) 2. **Select the Target Table:** Task Table. Then proceed to chart configuration. ![Steps Example](https://static-docs.nocobase.com/Solution/202411161926301731756390.png) ![Configuration Example](https://static-docs.nocobase.com/Solution/202411161928201731756500.png) #### 9.1.2 Configure Task Status Statistics If we want to count the number of tasks in different states, what should we do? First, we have to process the data: - Measures: Choose a unique field, such as the ID field to count. - Dimensions: Group data by **status**. Next, Chart Configuration: 1. Select a [bar](https://docs-cn.nocobase.com/handbook/data-visualization-echarts/bar) or [column chart](https://docs.nocobase.com/handbook/data-visualization-echarts/column). 2. Set the **X field** to "Status" and the **Y field** to "ID." *(Tip: Remember to choose the "Status" in dimension fields for better visual distinction.)* ![](https://static-docs.nocobase.com/20241208172705.png) #### 9.1.3 Multi-Dimensional Statistics: Tasks Per Person Want to view task counts per person and their statuses? Use dual dimensions by adding the "Assignee/Nickname" dimension. 1. Click "Run Query" in the top-left corner. ![](https://static-docs.nocobase.com/20241208172811.png) 2. If the result isn't what you expected, select **"isGroup"** to display a comparative breakdown of tasks by assignees. ![](https://static-docs.nocobase.com/20241208172901.png) 3. Meanwhile, if you want to display the overall number of stacked, you can choose “isStack”. In this way, we can see the percentage of tasks for each person + the overall task status! ![](https://static-docs.nocobase.com/20241208172933.png) ### 9.2 Data Filtering and Dynamic Display #### 9.2.1 Configure Data Filters Of course, we can further remove the “Canceled” and “Archived” data, just remove these two options in the left filter conditions, I believe you are very familiar with these conditions! ![](https://static-docs.nocobase.com/20241208173025.png) Once filtered, click confirm, exit configuration, and your first chart is ready! #### 9.2.2 [Duplicate Charts](https://docs.nocobase.com/handbook/data-visualization/user/chart-block#configure-block-operations) Need both **"isGroup"** and **"isStack"** charts without starting from scratch? Duplicate your chart: - Click the copy icon on the top-right of your first chart block. - Slide the wheel down, the second chart has appeared, drag and drop it to the right, remove the “isStack” configuration, change to “isGroup”. ![](https://static-docs.nocobase.com/Solution/202411161947481731757668.png) #### 9.2.3 Dynamic [Filtering](https://docs.nocobase.com/handbook/data-visualization/user/filter) For interactive [filtering](https://docs.nocobase.com/handbook/data-visualization/user/filter) of task data: Of Course! We open "Filter" below the chart data block, and the filter box has appeared above. We show the desired fields and set the filtering conditions for the fields. (For example, change the date field to "between") ![Filter Example](https://static-docs.nocobase.com/Solution/202411161948261731757706.png) ![](https://static-docs.nocobase.com/Solution/202411161951261731757886.png) ![](https://static-docs.nocobase.com/Solution/202411161952281731757948.png) #### 9.2.4 Creating Custom Filter Fields What if we also want to include “Canceled” and “Archived” data in special cases, and support dynamic filtering and set filtering defaults? Let's create a [customized filter field](https://docs.nocobase.com/handbook/data-visualization/user/filter#custom-fields)! > You can easily configure filters by selecting fields from associated data tables or creating custom fields.(Only available within chart block) > > Options include editing field titles, descriptions, operators, and setting default values (e.g., the current user or date), making filters more tailored to your needs. 1. fill in the title of the “Status”. 2. Leave the Source field blank. 3. Select “Checkbox” for the component. 4. Options are filled in according to the value of the Status attribute when the database is created (note that the order of attributes here is Option Label - Option Value). ![](https://static-docs.nocobase.com/Solution/202411161958151731758295.png) Create successfully, click “Set Default Value”, select the option we need. ![](https://static-docs.nocobase.com/Solution/202411162000141731758414.png) ![202411162000481731758448.png](https://static-docs.nocobase.com/Solution/202411162000481731758448.png) After setting the default value, go back to the chart configuration, change the filter condition to “Status - is any of - Current filters / status”, and then confirm! (Both charts should be changed.) ![](https://static-docs.nocobase.com/Solution/202411162001431731758503.png) Done, let's filter test, the data has been perfectly rendered. ![202411162003151731758595.png](https://static-docs.nocobase.com/Solution/202411162003151731758595.png) ### 9.3 Dynamic Links and Task Filtering A powerful feature: clicking on a statistic to jump to filtered tasks. Let’s make it happen. #### 9.3.1 Using the "Not started" Example, Create a [Statistical Chart](https://docs.nocobase.com/handbook/data-visualization/antd/statistic) 1. Set the Measures to **ID - Count**. 2. Add a filter: Status = "Not Started" 3. Set container name to "Not Started", Chart Type to "Statistic", and leave chart name blank. ![](https://static-docs.nocobase.com/Solution/202411162011451731759105.png) The unstarted stats have been successfully displayed. Let's make five copies by state and drag them to the top. ![](https://static-docs.nocobase.com/Solution/202411162017471731759467.png) #### 9.3.2 Configure Link Filtering 1. Go back to the page containing the task management table block and observe the link format in the browser's address bar (usually something like `http://xxxxxxxxx/admin/0z9e0um1vcn`). ![](https://static-docs.nocobase.com/Solution/202411162046301731761190.png) Assume `xxxxxxxxx` represents your website domain, and `/admin/0z9e0um1vcn` is the path. (We only need to find the last `/admin`.) 2. Copy part of the link: - We need to perform a link jump. To do this, we first extract a specific portion of the link. - Copy from the text after `admin/` (excluding `admin/`) to the end of the link. For example, in this case, the portion to copy is: `0z9e0um1vcn`. ![](https://static-docs.nocobase.com/Solution/202411162048571731761337.png) Move the cursor over "Not Started," and you’ll notice the cursor changes to a hand icon. Click on it to jump successfully. ![](https://static-docs.nocobase.com/241207gif_click_filter.gif) 3. Configure the chart link:Now, let’s add a filter parameter to the link. Do you remember the database identifier for task status? We’ll need to append this parameter to the end of the link to further filter tasks. - Add `?task_status=Not started` to the end of the link. Your link will then look like: `0z9e0um1vcn?task_status=Not started`. ![](https://static-docs.nocobase.com/Solution/202411162106351731762395.png) > **Understanding URL Parameters:** When adding parameters to a URL, there are formatting rules to follow: > > - **Question mark (?):** Indicates the start of the parameters. > - **Parameter name and value:** Format as `parameter_name=parameter_value`. > - **Multiple parameters:** Use the `&` symbol to connect them. For example: > `http://xxxxxxxxx/admin/hliu6s5tp9x?status=todo&user=123`. > In this example, `user` is another parameter name, and `123` is its corresponding value. 4. Go back to the page, click to jump, and you’ll find the desired parameters are now included in the URL. (Like `http://xxxxxxxxx/admin/0z9e0um1vcn` normally) ![](https://static-docs.nocobase.com/Pasted%20image%2020241207124358.png) #### 9.3.3 [Associating URL Filter Conditions](https://docs.nocobase.com/handbook/ui/variables#url-search-params) Why hasn't the table changed yet? Don't worry, let's finish the last step together! - Go back to the form block configuration and click on “Set data range”. - Select “Status” equal to “URL search params / status”. Click on “Confirm” and the filtering is successful! ![f8c9b2d7f64cf1da4daaa80497235f92.png](https://static-docs.nocobase.com/f8c9b2d7f64cf1da4daaa80497235f92.png) ![](https://static-docs.nocobase.com/Pasted%20image%2020241207124401.png) ![](https://static-docs.nocobase.com/20241207124814.png) ![](https://static-docs.nocobase.com/20241207124914.png) ### 9.4 [Data Visualization](https://docs.nocobase.com/handbook/data-visualization): Stunning Charts > **Data Visualization: [ECharts](https://docs.nocobase.com/handbook/data-visualization-echarts) (Commercial Plugin)** > ECharts offers more advanced and customizable configuration options, such as "[Line Chart](https://docs.nocobase.com/handbook/data-visualization-echarts/line) (Multi-Dimension)," "[Radar Chart](https://docs.nocobase.com/handbook/data-visualization-echarts/radar)," "[Word Cloud](https://docs.nocobase.com/handbook/data-visualization-echarts/wordcloud)," and more. If you want to access more chart configuration options, you can enable "Data Visualization: ECharts"! #### 9.4.1 Quickly Configure a Stunning [Radar Chart](https://docs.nocobase.com/handbook/data-visualization-echarts/radar) ![](https://static-docs.nocobase.com/Solution/202411162116541731763014.png) If you notice data overlap, remember to adjust the size or radius to ensure that all information is clearly displayed! ![](https://static-docs.nocobase.com/Solution/202411162119541731763194.png) ![202411162121201731763280.png](https://static-docs.nocobase.com/Solution/202411162121201731763280.png) After configuration, simply drag and adjust the display style to complete! ![](https://static-docs.nocobase.com/Solution/202411162124151731763455.png) #### 9.4.2 More chart containers Here are more charts for you to explore. ##### [Word Cloud](https://docs.nocobase.com/handbook/data-visualization-echarts/wordcloud) ![](https://static-docs.nocobase.com/20241207125043.png) ##### [Funnel](https://docs.nocobase.com/handbook/data-visualization-echarts/funnel) ![](https://static-docs.nocobase.com/Solution/202411162130021731763802.png) ##### [Multiple Indicators (Dual Axes, Echarts Line)](https://docs.nocobase.com/handbook/data-visualization/antd-charts/dual-axes) For bi-axial charts you can add more indicators ![](https://static-docs.nocobase.com/Solution/202411162133541731764034.png) ##### [Diverging bar](https://docs-cn.nocobase.com/handbook/data-visualization-echarts/diverging-bar) ![](https://static-docs.nocobase.com/Solution/202411162136401731764200.png) ### 9.5 Mini Challenge 1. Configure URL parameters for the remaining statuses: "In Progress," "Pending Review," "Completed," "Canceled," and "Archived." 2. Set up a "Assignee" multi-select field, just like the "Status" field, with a default value of the current user’s nickname. Looking forward to seeing you in the [next chapter](https://www.nocobase.com/en/tutorials/project-tutorial-task-dashboard-part-2)! --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions and support. --- url: /tutorials/v1/11-dashboard-filters.md --- # Chapter 10: Dashboard Filters & Conditions In this chapter, we will guide you step by step through the next part of creating a task dashboard. If you have any questions, feel free to consult the forums at any time. Let’s start this exploration journey by reviewing the content from the previous chapter! ### 10.1 Reveal the Previous Chapter's Solutions #### 10.1.1 Status and Links First, we need to add navigation links for data in different statuses to enable quick access. Below is the link structure for each status: (Assume the base link is `http://xxxxxxx/admin/hliu6s5tp9xhliu6s5tp9x`) ##### Mystery Solved | Status | Link | | -------------- | ------------------------------------------------------ | | Not started | hliu6s5tp9xhliu6s5tp9x?task_status=Not started
| | In progress | hliu6s5tp9xhliu6s5tp9x?task_status=In progress
| | To be reviewed | hliu6s5tp9xhliu6s5tp9x?task_status=To be reviewed
| | Completed | hliu6s5tp9xhliu6s5tp9x?task_status=Completed
| | Cancelled | hliu6s5tp9xhliu6s5tp9x?task_status=Cancelled
| | Archived | hliu6s5tp9xhliu6s5tp9x?task_status=Archived
| #### 10.1.2 Adding a Multi-Select Option for Assignee 1. **Create a [Custom Fields](https://docs.nocobase.com/handbook/data-visualization/user/filter#custom-fields):** Add a "Assignee" field of type "multi-select," and populate it with members’ nicknames (or usernames) to facilitate quick assignment of tasks. ![](https://static-docs.nocobase.com/Solution/441734278924202416000812.png) 2. **Configure the Report:** Set up “Task Assignee / Nickname (Username)- contains - Current Filter / Assignee” as a filter condition to quickly locate tasks associated with the selected assignee. ![](https://static-docs.nocobase.com/Solution/291734279089202416001112.png) Perform several filter tests to confirm proper functionality. ![](https://static-docs.nocobase.com/Solution/431734279283202416001412.png) --- ### 10.2 Associating the Dashboard with Users Content can be displayed based on different users. Here’s how to do it: 1. **Set the Default Value of the "Assignee" Field to "Current User/Nickname (Username)":** This allows the system to automatically display tasks related to the current user, improving operational efficiency. ![](https://static-docs.nocobase.com/Solution/441734279344202416001512.png) ![](https://static-docs.nocobase.com/Solution/101734279430202416001712.png) 2. **After Refreshing the Page:** The dashboard will automatically load data associated with the currently logged-in user. (Don’t forget to add user filter conditions to the relevant charts.) ![](https://static-docs.nocobase.com/Solution/191734279499202416001812.png) --- ### 10.3 Redesigning Task Filtering Some users may notice a design flaw: When filtering by status directly in the table block's "Set the Data Scope," tasks are prematurely restricted to a specific status. When attempting to filter by a different status later, the data appears empty! Here’s how to resolve this by changing the filtering approach: 1. **Remove the Data Filtering Method:** Prevent status data from being locked into a specific range, allowing for flexible filtering needs. ![](https://static-docs.nocobase.com/Solution/591734279599202416001912.png) 2. **Configure Default Values in the Filtering Form Block:** Do you remember our configured [Filter blocks](https://docs.nocobase.com/handbook/ui/blocks/filter-blocks/form)? Create a form block for task table filtering block and configure **status** and other necessary fields to populate variables from the URL. (Ensure it connects to the task table block that needs filtering.) - Set the default value of the status field to `URL search params/task_status`. ![](https://static-docs.nocobase.com/Solution/521734279772202416002212.png) ![](https://static-docs.nocobase.com/Solution/331734279873202416002412.png) ![](https://static-docs.nocobase.com/Solution/201734280160202416002912.png) 3. **Test the New Filtering Functionality:** Dynamically switch status filters as needed. ![](https://static-docs.nocobase.com/Solution/demoE3v1-50.gif) - **Optional:** To focus each user on their tasks, set the default value of the "Assignee" field to "Current User." --- ### 10.4 News, Notifications, and Information Highlights Let’s enhance the document library to display essential information on the dashboard. In long-term document management, diverse requirements often emerge as the volume of materials grows: - **News:** Highlight project updates, achievements, and milestones. - Temporary announcements/reminders. #### 10.4.1 Hot Information (News) 1. **Add a "Hot Information" Field:** Add a checkbox field named "Hot Information" in the document table to mark whether the document is significant news. ![](https://static-docs.nocobase.com/Solution/081734280628202416003712.png) 2. **Add and Select Document Information:** Choose a document and enable the "Hot Information" checkbox in the edit form. ![](https://static-docs.nocobase.com/Solution/331734280773202416003912.png) 3. **Create a "List" Block:** Go back to the dashboard and create a ["List" block](https://docs.nocobase.com/handbook/ui/blocks/data-blocks/list) to display the document table. ![](https://static-docs.nocobase.com/Solution/361734280836202416004012.png) Drag it to the right, showing "Creation Date" and "Title." Adjust field widths and disable "Show Title." ![](https://static-docs.nocobase.com/Solution/011734281041202416004412.png) 4. **Display Hot Information:** In order to reflect real-time, we can show the time at the same time. ![](https://static-docs.nocobase.com/Solution/021734281102202416004512.png) ![](https://static-docs.nocobase.com/Solution/131734281173202416004612.png) Arrange in descending order by creation date to showcase the latest news. ![](https://static-docs.nocobase.com/Solution/551734281335202416004812.png) ![](https://static-docs.nocobase.com/Solution/291734281369202416004912.png) A simple hot information section is now ready, allowing team members to keep up with critical project progress! ![](https://static-docs.nocobase.com/Solution/301734281430202416005012.png) #### 10.4.2 Announcements Notification The next step involves creating a straightforward announcement feature. This temporary notification doesn’t require long-term display or project tracking—it’s just for reminders or alerts about temporary matters. 1. **Create a [Markdown Block](https://docs.nocobase.com/handbook/ui/blocks/other-blocks/markdown):** Use Markdown syntax to add announcement content to any area of the dashboard. ![](https://static-docs.nocobase.com/Solution/231734281783202416005612.png) For practical use of Markdown, you can refer to our official Demo, official documentation, or the[ “Lightweight Documentation” tutorial](https://www.nocobase.com/en/tutorials). As an simple example, based on the HTML language written "A gorgeoAus announcement" to demonstrate the power of the [Markdown block](https://docs.nocobase.com/handbook/ui/blocks/other-blocks/markdown). - Sample code: ```html

Important Bulletin

Dear Colleagues:

In order to improve work efficiency, we will conduct an all-staff training in November 10, 2024.

Thank you for your cooperation!

With best wishes

Management Team

``` ![](https://static-docs.nocobase.com/Solution/011734282061202416010112.png) ### 10.5 Summary By following the configuration steps above, we successfully created a personalized dashboard that enables team members to efficiently manage tasks, monitor project progress, and promptly receive announcements and notifications. From status filtering and Assignee settings to hot information display, these features aim to optimize user experience and enhance system convenience and flexibility. With our personalized dashboard now ready, we invite you to explore and adapt it to your unique needs, let us step into [next chapter](https://www.nocobase.com/en/tutorials/project-tutorial-subtasks-and-work-hours-calculation)! --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions and support. --- url: /tutorials/v1/12-subtasks-workhours.md --- # Chapter 11: Subtasks & Work Hours Hello, team! We've finally reached a new chapter! As our business expands, tasks have become increasingly numerous and complex. We’ve realized that simple task management is no longer sufficient. It's time to manage tasks more intricately by breaking them into multiple levels, which will help everyone complete them more efficiently. ### 11.1 Task Planning: From Global to Local We’ll divide complex tasks into multiple manageable subtasks, enabling progress tracking for a clear understanding of task completion status. Using multi-level management, we’ll organize these subtasks effectively. Let’s dive into planning! --- ### 11.2 Creating a Subtask Table #### 11.2.1 Designing the Subtask Structure First, we’ll create a "Subtask Table" ([**Tree collection**](https://docs.nocobase.com/handbook/collection-tree)). Subtasks will have attributes similar to main tasks, such as "Task Name," "Status," "Assignee," and "Progress." Additional fields like comments or documents can be added as needed. To link subtasks to main tasks, we’ll establish a **many-to-one** relationship, assigning each subtask to a main task. A reverse relationship allows viewing or managing subtasks directly within the main task. ![Tree Table Structure Example](https://static-docs.nocobase.com/Solution/561734342536202416174812.png) > 💡 **Tip:** It’s recommended to create subtasks through a linked block on the main task page for a more streamlined process! #### 11.2.2 Displaying Subtasks in the Task Management Interface In the task management interface, set the "Task Table" view mode to [**Page View**](https://docs.nocobase.com/handbook/ui/pop-up#page). ![](https://static-docs.nocobase.com/Solution/171734342677202416175112.png) Create a new **"SubTasks"** tab within the page and add the subtask table, using the tree structure for display. This way, subtasks can be managed and viewed on the same page. ![](https://static-docs.nocobase.com/Solution/451734343605202416180612.png) --- ### 11.3 Work Hour Comparison Chart: Estimating Overall Work Hours and Progress (Optional) Next, we strike while the iron is hot, to make the work details of the task and the work comparison chart, in order to estimate the overall work time and task progress. #### 11.3.1 Adding Time and Work Hour Fields In the subtask table, add these fields: - **Start Date** - **End Date** - **Estimate Hours** - **Remain Hours** ![Work Hour Fields](https://static-docs.nocobase.com/Solution/021734344102202416181512.png) These fields allow dynamic calculation of task duration and work hours. #### 11.3.2 Calculating Task Duration Create a new [formula field](https://docs.nocobase.com/handbook/field-formula), **"Days,"** in the subtask table to calculate task duration. ![](https://static-docs.nocobase.com/Solution/271734344367202416181912.png) Formula Calculation Options: - Math.js > Using the [math.js](https://mathjs.org/) library, you can compute complex numeric formulas. > - Formula.js > Uses [Formula.js](https://formulajs.info/functions/) library to calculate common formulas, if you are familiar with Excel formulas, this will be easy for you! > - String Templates > As the name suggests, it is a means of splicing characters, we usually need dynamic descriptions, numbering and so on, you can use this form of splicing! > For this task, use the **Formula.js** library to calculate common formulas. The formula for task duration: ```plaintext DAYS(End Date, Start Date) ``` Ensure all formula elements are in **lowercase English** to avoid errors. ![Dynamic Task Duration Example](https://static-docs.nocobase.com/Solution/471734356267202416213712.png) Check the page, and you’ll see that the duration adjusts dynamically based on the start and end dates! ![Task Duration Calculation](https://static-docs.nocobase.com/Solution/561734356516202416214112%20(2).png) --- ### 11.4 Daily Work Hour Reporting: Tracking Actual Progress (Optional) #### 11.4.1 Creating a Daily Work Hour Reporting Collection Create a new table for daily work hour reporting. Add fields such as: - **Day Hours** (integer recommend) - **Date** - **Ideal Hours** (integer) - **Associated Sub-task** ([Many-to-One](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations/m2o) relationship with subtasks). ![Daily Work Hour Reporting Table](https://static-docs.nocobase.com/Solution/421734356922202416214812.png) #### 11.4.2 Displaying Daily Work Hours in the Subtask Page Return to the subtask editing page, and set the daily work hour table as a [Sub table](https://docs.nocobase.com/handbook/ui/fields/specific/sub-table) for display. Drag other relevant fields into the layout. This allows easy data entry and review directly on the subtask page. ![Daily Work Hour Subtable](https://static-docs.nocobase.com/Solution/581734357538202416215812.png) ![Completed Subtask Page Example](https://static-docs.nocobase.com/Solution/421734357822202416220312.png) --- ### 11.5 Key Calculations & Linking Rules (Optional) In order to more accurately estimate the task progress and remaining work hours, we next perform some key configurations. #### 11.5.1 Setting[ Required Fields](https://docs.nocobase.com/handbook/ui/fields/field-settings/required) for Subtasks Mark **Start Date**, **End Date**, and **Estimated Work Hours** as [required item](https://docs.nocobase.com/handbook/ui/fields/field-settings/required) to ensure data completeness for accurate calculations. #### 11.5.2 Setting Complete Percent and Remaining Time [Linkage Rules](https://docs.nocobase.com/handbook/ui/actions/action-settings/linkage-rule) Add these calculated fields in the subtask table: 1. **Completion Ratio**: Daily work hours sum / Estimated work hours. ```html SUM(Current Form/Daily Work/Day Hours) / Current Form/Estimate hours ``` 2. **Remaining Work Hours**: Estimated work hours - Daily work hours sum. ```html Current Form/Estimate hours - SUM(Current Form/Daily Work/Day Hours) ``` ![](https://static-docs.nocobase.com/Solution/281734358108202416220812.png) For **Ideal Work Hours** in daily work hour linking rules: ```html [Current Form/Estimate hours] / [Current Form/Sub Task Duration] ``` ![Ideal Work Hour Rule](https://static-docs.nocobase.com/Solution/471734358187202416220912.png) ![](https://static-docs.nocobase.com/Solution/041734358384202416221312.png) With this, you can calculate task progress and remaining hours in real time. ![Progress Example](https://static-docs.nocobase.com/Solution/411734358601202416221612.png) ### 11.6 Creating a Task Progress Chart (Optional) #### 11.6.1 Setting Up a Task Progress [Chart](https://docs.nocobase.com/handbook/data-visualization/user/chart-block) Create a new chart block for counting the changes in sum of **Day Hours** and sum of **Ideal Hours** and displaying the task progress based on the date dimension. Limit [Associated Tasks/Id] to be equal to when [Previous Popup Records/ID] to ensure that the progress chart reflects the true state of the current task. ![Chart Setup](https://static-docs.nocobase.com/Solution/531734359813202416223612.png) ![Configured Chart Example](https://static-docs.nocobase.com/Solution/031734360123202416224212.png) #### 11.6.2 Displaying Key Info & Progress Finally, remember our [Markdown block](https://docs.nocobase.com/handbook/ui/blocks/other-blocks/markdown), where we show the basic information and progress changes of the task through the `markdown` block. The progress percentage is rendered using the [`Handlebars.js`](https://docs.nocobase.com/handbook/template-handlebars) template: ![](https://static-docs.nocobase.com/Solution/521734361132202416225812.png) ```html **Progress of Last Update:**

{{floor (multiply $nRecord.complete_percent 100)}} %

``` This uses [Handlebars.js](https://docs-cn.nocobase.com/handbook/template-handlebars) for syntax, rendering progress percentages dynamically. --- ### 11.7 Summary Congratulations! You’ve successfully divided tasks into subtasks. With multi-level management, daily work hour reporting, and chart visualization, tracking progress becomes clearer and helps your team work more efficiently. Keep up the great work, Let's look forward to the [next chapter](https://www.nocobase.com/en/tutorials/project-tutorial-meeting-room-booking)! --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions and support. --- url: /tutorials/v1/13-meeting-rooms.md --- # Chapter 12: Meeting Room Booking & Workflows By now, you must be well-acquainted with **NocoBase**. In this chapter, we’ll implement a specific scenario: the **Meeting Rooms Management Module**. This module encompasses features such as **Meeting Rooms Booking** and **notifications**. We will build this module step by step, starting from scratch and gradually adding complexity. Let’s begin by designing the foundational database schema for this module. --- ### 12.1 Designing the Database Schema The database schema serves as the foundation of the Conference Management Module. In this section, we’ll focus on the **Meeting Rooms** collection and the **Booking** collection, and introduce relationships such as [Many-to-Many](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations/m2m) connections with users. ![](https://static-docs.nocobase.com/Solution/CRuKbVrnroSgGdxaVrBcvzSMnHd.png) #### 12.1.1 Conference Room Collection The Conference Room Collection stores basic information about all the conference rooms, including their name, location, capacity, and equipment. ##### Sample Collection Structure ```json Conference Rooms (Rooms) ID (Primary Key) Name (Single-line Text) Location (Long Text) Capacity (Integer) Equipment (Long Text) ``` #### 12.1.2 Booking Collection The Booking Collection records all the conference room bookings, including details such as the room, participants, time, and meeting agenda. ##### Sample Collection Structure ```json Bookings ID (Integer, Unique Primary Key) Room (Many-to-One, Foreign Key room_id linked to Room ID) Users (Many-to-Many, linked to User IDs) Start Time (Datetime (with time zone)) End Time (Datetime (with time zone)) Title (Single-line Text) Description (Markdown) ``` ##### [Many-to-Many](https://docs.nocobase.com/handbook/data-modeling/collection-fields/associations/m2m) Relationship The Booking Collection involves a **"Many-to-Many" relationship**: a user can attend multiple meetings, and a meeting can have multiple participants. For management purposes, a through collection named **booking_users** is created. ![](https://static-docs.nocobase.com/Solution/202411170441451731789705.png) --- ### 12.2 Building the Conference Management Module With the database schema ready, let’s implement the two collections and build the "Conference Management" module step by step. #### 12.2.1 Creating [Table Blocks](https://docs.nocobase.com/handbook/ui/blocks/data-blocks/table) First, add the “Meeting Management” module to the page, and create a **Meeting Room table block** and a **Appointment Form [table block](https://docs.nocobase.com/handbook/ui/blocks/data-blocks/table)**. Then create a [calendar block](https://docs.nocobase.com/handbook/calendar) for the appointment form, with the default view of the calendar set to “days”. ![](https://static-docs.nocobase.com/Solution/202411140500171731531617.png) ##### Setting up the Meeting Room form block association Associate the Meeting Room form block with the other two blocks so that you can automatically filter the appointments for that meeting room. Next, you can try out the filtering, adding, deleting, checking and changing functions to test the basic interaction of the module. > 💡**NocoBase Block Connection (Highly Recommended!!)** > > In addition to the filter block mentioned earlier, our table blocks can also be connected to other blocks to achieve click-based filtering functionality. > > As shown below, in the configuration of the Meeting Room table, we connected it to two blocks from other booking tables (Booking Table Block, Booking Calendar Block). ![](https://static-docs.nocobase.com/Solution/202411170506021731791162.png) > Once connected, clicking on the Meeting Room table filters the other two tables accordingly! Clicking the selected item again will cancel the selection. > > ![](https://static-docs.nocobase.com/2024120812346-block-connection-en2.gif) --- ### 12.3 Detecting Meeting Room Occupancy Once the page is configured, we need to add an important feature: detecting the occupancy of a meeting room. This feature will check if the target meeting room is occupied at the specified time period when creating or updating a meeting, in order to avoid booking conflicts. ![](https://static-docs.nocobase.com/project-management-meetings-er-en.drawio.svg) #### 12.3.1 Setting up a “pre-operation event” [Workflow](https://docs.nocobase.com/handbook/workflow) In order to perform the detection at the scheduled time, we use a special workflow - the [“pre-action event”](https://docs.nocobase.com/handbook/workflow-request-interceptor): - [**Pre-action events**](https://docs.nocobase.com/handbook/workflow-request-interceptor) (commercial plugin): perform a series of operations before adding, deleting, or changing data, which can be paused and intercepted in advance at any time, in a way that is very close to our daily code development process! ![](https://static-docs.nocobase.com/Solution/202411170548131731793693.png) #### 12.3.2 Configuring Nodes In the workflow for detecting occupancy, we need the following types of nodes: - [**Calculation nodes**](https://docs.nocobase.com/handbook/workflow/nodes/calculation) (data transformation logic to handle modifications, additions) - [**SQL operations**](https://docs.nocobase.com/handbook/workflow/nodes/sql) (to perform SQL queries) - [**JSON query**](https://docs.nocobase.com/handbook/workflow/nodes/json-query) (commercial plugin for parsing JSON data) - [**Response message**](https://docs.nocobase.com/handbook/workflow/nodes/response-message) (commercial plug-in, used to return alert messages) --- #### 12.3.3 Binding the Appointment Collection and Configuring Triggers Now, let's bind the appointment collection, select “Global Mode” for the trigger mode, and select the operation type as Create Record and Update Record. ![](https://static-docs.nocobase.com/Solution/202411170549121731793752.png) --- ### 12.4 Configure the [Calculation node](https://docs.nocobase.com/handbook/workflow/nodes/calculation) #### 12.4.1 Creating the “Convert Blank ID to -1” operator node We start by creating an operator node that converts a blank ID to -1. The operator node can convert the variable in the way we want, and offers the following three forms of operation: - **Math.js** (refer to [ Math.js](https://mathjs.org/)) - **Formula.js** (reference [ Formula.js](https://formulajs.info/functions/)) - **String template** (for data splicing) Here, we use **Formula.js** for numerical judgment: ```html IF(NUMBERVALUE([Trigger variables/Parameter/Values sbumitted/ID], '', '.') ,[Trigger variables/Parameter/Values submitted/ID], -1) ``` ![](https://static-docs.nocobase.com/Solution/202411170551111731793871.png) --- ### 12.5 Creating the [SQL Operation Node](https://docs-cn.nocobase.com/handbook/workflow/nodes/sql) Next, create the SQL operation node, execute the query statement, and check the available meeting rooms: #### 12.5.1 Querying available conference room SQL statements ```sql -- Query all available meeting rooms. SELECT r.id, r.name FROM rooms r LEFT JOIN booking b ON r.id = b.room_id AND b.id <> {{$jobsMapByNodeKey.3a0lsms6tgg}} -- exclude current bookings AND b.start_time < '{{$context.params.values.end_time}}' -- start time is before the query end time AND b.end_time > '{{$context.params.values.start_time}}' -- end time is after the query start time WHERE b.id IS NULL; ``` > SQL Note: Variables will be directly substituted into the sql statement, please check the variables carefully to avoid SQL injection. Add single quotes where appropriate. Where the variables are respectively: {{$jobsMapByNodeKey.3a0lsms6tgg}} Represents the result of the previous node, [node data/convert blank ID to -1] {{$context.params.values.end_time}} represents [Trigger variables/Parameters/Values submitted/End Time] {{$context.params.values.start_time}} for [Trigger variable/Parameter/alues submitted/Start Time]. #### 12.5.2 Testing the SQL Our goal is to query all rooms that do not conflict with the target timestamp. In the meantime, you can click “Test run” below to change the variable values and debug the SQL. ![](https://static-docs.nocobase.com/Solution/202411170554241731794064.png) ![](https://static-docs.nocobase.com/Solution/202411170556061731794166.png) --- ### 12.6 [JSON Parsing](https://docs.nocobase.com/handbook/workflow/nodes/json-query) #### 12.6.1 Configuring the [JSON Query Node](https://docs.nocobase.com/handbook/workflow/nodes/json-query) Through the test in the previous step, we can observe that the result is in the following form, at this time you need to enable the [**JSON query node plugin**](https://docs.nocobase.com/handbook/workflow-json-query): ```json [ { “id": 2, “name": ”Meeting Room 2” }, { “id": 1, ‘name’: ‘Meeting Room 1’ }, { “name": ”Meeting Room 1” } ] ``` > JSON is parsed in three ways, which are: > [JMESPath](https://jmespath.org/) > [JSON Path Plus](https://jsonpath-plus.github.io/JSONPath/docs/ts/) > [JSONata](https://jsonata.org/) Here we choose any one of them, such as [JMESPath](https://jmespath.org/) format, we need to filter the list of all available conference room names, so the expression is filled: ```sql [].name ``` The attribute mapping configuration is for the list of objects and is not necessary at the moment, so it can be left out. ![](https://static-docs.nocobase.com/Solution/202411170600051731794405.png) ### 12.7 [Condition Judgment](https://docs.nocobase.com/handbook/workflow/nodes/condition) Configure the Conditional Judgment node to determine whether the current conference room is in the list of available conference rooms. According to the judgment result of **Yes** or **No**, configure the response message respectively: **Condition Setup**: Use the "Basic" operation for the condition: ```json [Node result/Analyze Roome Name] contains [Trigger variables/Parameter/Values submitted/room/Name] ```` ![](https://static-docs.nocobase.com/Solution/202411170601501731794510.png) #### 12.7.1 Yes: Configure Success Message Enable the [**Workflow: Response Message Plugin**](https://docs.nocobase.com/handbook/workflow-response-message): ```json [Trigger Variables/Parameters/Values submitted/room/Name] is available. Booking successful! ``` ![](https://static-docs.nocobase.com/Solution/202411170603551731794635.png) #### 12.7.2 No: Configure Failure Message For the case where the meeting room is unavailable: ```json The requested meeting room is unavailable. Available meeting rooms: [Node Data/Analyze Room Name] ``` Note: Ensure that the **End Workflow** node is configured to manually terminate the workflow in case of failure. ![](https://static-docs.nocobase.com/Solution/202411170606321731794792.png) --- ### 12.8 Function Testing and Debugging Details Now, we move to the **final testing phase** of the meeting management system. This phase ensures that the workflow can correctly detect and prevent conflicting meeting room bookings. #### 12.8.1 Adding a Booking with Conflicting Times Firstly, let's try to add a meeting that conflicts with an existing scheduled time to see if the system blocks the operation and pops up an error message. - **Set a Conflicting Booking Period** Add a new booking in "Meeting Room 1" for the time period: `2024-11-14 00:00:00 - 2024-11-14 23:00:00` This time span covers the entire day, intentionally overlapping with existing bookings. Existing Bookings in "Conference One" 1. `2024-11-14 09:00:00 - 2024-11-14 12:00:00` 2. `2024-11-14 14:00:00 - 2024-11-14 16:30:00` These periods overlap with the new booking request. (`2024-11-14 00:00:00 - 2024-11-14 23:00:00`) That's been overlapped. Therefore, based on logical judgment, the system should detect the time conflict and block this booking. - Submit the booking and validate the feedback We click the **Submit** button and the system will execute the detection process in the workflow: **Successful Feedback:** After submitting, the system pops up a conflict alert, indicating that the detection logic is working properly. The page Feedback Successfully indicates that we were unable to complete this booking. ![](https://static-docs.nocobase.com/Solution/202411170616271731795387.png) --- #### 12.8.2 Adding a Booking with No Conflicts Next test conflict-free booking ~ Ensure that we can successfully book a meeting room when the meeting times do not overlap! - Setting up a Conflict-Free Booking Let's choose a time period with no conflicts, for example `2024-11-10 16:00:00 - 2024-11-10 17:00:00`. This time period does not overlap with existing bookings and therefore meets the meeting room booking requirements. - To submit a non-conflicting reservation Click the **Submit** button and the system performs the workflow detection logic again: **Verify it together:** Submitted successfully! The system displays a “Booking Success” message. The system displays a “Reservation Success” message. This indicates that the reservation function is also working correctly in the case of a conflict-free reservation. The system displays the “Booking Success” message. ![](https://static-docs.nocobase.com/Solution/202411140621101731536470.png) #### 12.8.3 Modifying Existing Booking Times In addition to adding new bookings, you can also test by modifying the time of existing bookings. For instance, change an existing meeting time to a non-conflicting period and submit the update. I'll leave it to you. --- ### 12.9 Dashboard Optimization and Personal Schedule Panel Once functionality tests are complete, optimize the dashboard to improve user experience. #### 12.9.1 Adjusting Dashboard Layout Reorganize the dashboard content based on user interaction habits for easier data viewing.Enhance the user experience by adding a **Personal Schedule Panel**: 1. **Create a "Personal Schedule" Section** Add a calendar or list block to display each user's individual meeting schedule. 2. **Set Member Defaults** Configure the default member value to the current user. This ensures that when a user accesses the dashboard, their related meetings are displayed by default. Further optimize the user experience in the meeting management module. After configuration, the dashboard becomes more intuitive and feature-rich, enabling users to better navigate and manage their meetings. ![](https://static-docs.nocobase.com/Solution/202411140634111731537251.png) ![](https://static-docs.nocobase.com/Solution/202411140635081731537308.png) With these steps, we have successfully implemented and optimized the core functionalities of the conference room booking management module! Enjoy the process of mastering NocoBase’s modular system-building capabilities. --- Keep exploring and creating endless possibilities! If you encounter any issues along the way, don’t forget to check the [NocoBase Documentation](https://docs.nocobase.com/) or join the [NocoBase Community](https://forum.nocobase.com/) for discussions and support. --- url: /tutorials/v1/14-user-review.md --- # Implementing User Registration Review This document provides two solutions for implementing user registration auditing, designed for different business scenarios: * **Solution One**: Suitable for scenarios that require a simple and quick registration audit process. This solution leverages the system's default new user registration functionality by assigning every new user a “Guest” role with no permissions, which is later manually audited and updated by an administrator. * **Solution Two**: Suitable for scenarios requiring a flexible, customizable registration audit process. By designing a dedicated application information collection, configuring an audit workflow, and activating the [Public Form](https://docs.nocobase.com/handbook/public-forms) plugin, the entire process—from registration application submission to automatic creation of new users—is managed. ![](https://static-docs.nocobase.com/20250219144628.png) --- ## 1. Solution One: Using a No-Permission “Guest” Role ### 1.0 Applicable Scenario This is applicable when the registration audit requirements are simple and the built-in registration feature is used, with the user being manually audited in the backend. ### 1.1 Enable Password Authentication and Allow User Registration #### 1.1.1 Access the User Authentication Page First, verify whether the user registration feature is enabled. In the system settings, navigate to the **[User Authentication](https://docs.nocobase.com/handbook/auth/user)** page, which manages all identity authentication channels such as “Account Password Login”, “[Google Login](https://docs.nocobase.com/handbook/auth-oidc/example/google)”, etc. (expandable via plugins). ![](https://static-docs.nocobase.com/20250208164554.png) The registration feature toggle is located here: ![](https://static-docs.nocobase.com/20250219142005.png) ### 1.2 Set the Default Role (Core) #### 1.2.1 Create a “Guest” Role The system’s registration feature is enabled by default, but the default role might not meet your requirements. Therefore, first create a “Guest” role in the **Role List** to be used as the default role. All new registered users will automatically be assigned this role and will have no permissions. ![](https://static-docs.nocobase.com/20250208163521.png) ### 1.3 Configure the Registration Audit Interface (Core) Switch to edit mode in the backend and configure a simple table block. Select the User collection to display and manage registered users. ![](https://static-docs.nocobase.com/20250208165406.png) ### 1.4 Test the Registration Audit Process and Manually Update Roles * After a new user registers, the page initially shows blank: ![](https://static-docs.nocobase.com/20250219142105.png) * In the management interface, for users whose application information is correct, manually change their role to the designated role to complete the audit. ![](https://static-docs.nocobase.com/20250219142155.png) ### 1.5 Configure the Prompt Page (Optional) #### 1.5.1 Create a Page (e.g., “Registration Successful”) and Enter Prompt Information > **Optional Step**: You can add a friendly prompt on this blank page, such as “Your account is under review, please wait patiently for approval,” to inform the user of the current status. ![](https://static-docs.nocobase.com/Pasted%20image%2020250208231631.png) #### 1.5.2 Assign Permissions to the Prompt Page Next, go to the user permission management configuration and assign this page to the “Guest” role. After successful registration, the system will automatically redirect to this page. ![](https://static-docs.nocobase.com/20250211223123.png) ### 1.6 Extend User Collection Fields (Optional) > **Optional Step**: If you need to collect additional information during registration to assist with the audit, add relevant fields (for example, “Application Reason” or “Invitation Code”) in the user collection. This step can be omitted if only basic registration auditing is needed. #### 1.6.1 Add Application Field Enter the **User Collection** and add a field to record the applicant’s reason for registration or invitation code information. ![](https://static-docs.nocobase.com/20250208164321.png) #### 1.6.2 Enable the Field in “User Authentication” ![](https://static-docs.nocobase.com/20250219142231.png) After configuration, go to the login page and click **Register Account** to see the corresponding field in the registration form (if the optional field is configured, otherwise only the basic form will be displayed). ![](https://static-docs.nocobase.com/20250219142306.png) #### 1.6.3 Add Corresponding Fields to the Audit Page Add these fields to the audit page as well so that user roles can be audited and modified in real time. ![](https://static-docs.nocobase.com/20250208165622.png) --- ## 2. Solution Two: Disable Direct Registration and Use an Audit Intermediate Collection ### 2.0 Applicable Scenario This solution is suitable for scenarios that require a more flexible and customized registration audit process. It uses an independent application information collection, workflow configuration, and [Public Form](https://docs.nocobase.com/handbook/public-forms) plugin to realize the complete process—from user registration application submission to the automatic creation of users. The core steps ensure basic functionality and can be further expanded as needed. ### 2.1 Preliminary Preparation (Core) #### 2.1.1 Design the Application Information Collection ##### 2.1.1.1 Create the “Application Information” Collection * **Create the Collection** In the NocoBase backend, create a new collection for storing user registration application information. * **Configure the Fields** Add the following fields to the collection, ensuring that the field types and descriptions are correct: | Field Display Name | Field Name | Field Interface | Description | | ---------------------- | ------------------ | ---------------- | ---------------------------------------------------------------------------- | | **ID** | id | Integer | Automatically generated by the system; unique record ID | | **Username** | username | Single line text | The applicant's username | | **Email** | email | Email | The applicant's email address | | **Phone** | phone | Phone | The applicant's contact number | | **Full Name** | full_name | Single line text | The applicant's full name | | **Application Reason** | application_reason | Long text | The reason or explanation provided by the applicant | | **User Type** | user_type | Single select | Specifies the future user type (e.g., email registration, open registration) | | **Status** | status | Single select | Current status of the application (e.g., pending, approved, rejected) | | **Initial Password** | initial_password | Single line text | The initial password for the new user (default: nocobase) | | **Created at** | createdAt | Created at | Timestamp of record creation by the system | | **Created by** | createdBy | Created by | The creator of the record as logged by the system | | **Last updated at** | updatedAt | Last updated at | Timestamp of the last update by the system | | **Last updated by** | updatedBy | Last updated by | The last person who updated the record | * **Preview the Collection Structure** Refer to the image below to confirm that the collection structure is configured correctly: ![](https://static-docs.nocobase.com/20250208145543.png) ##### 2.1.1.2 Data Entry and Display * **Configure the Audit Interface** In the main interface, set up a management interface called “Registration Application Audit” to display the submitted application information. * **Enter Test Data** Enter test data in the management interface to ensure that the data is displayed correctly. ![](https://static-docs.nocobase.com/20250208151429.png) ### 2.2 Workflow Configuration This section explains how to configure the workflow to automatically create new users after the application is approved. #### 2.2.1 Create the Audit Workflow ##### 2.2.1.1 Create a New Workflow * **Access the Workflow Interface** In the NocoBase backend, go to the workflow configuration page and select “Create New Workflow.” * **Select the Trigger Event** You can choose either a “[Post-Operation](https://docs.nocobase.com/handbook/workflow/triggers/post-action)” or “[Pre-Operation](https://docs.nocobase.com/handbook/workflow/triggers/pre-action)” event; here, we use a pre-operation event as an example. * **Configure Workflow Nodes** Create a new “Create New User” node that converts the current form data into new user data, setting up field mapping and processing logic. Refer to the image below: ![](https://static-docs.nocobase.com/20250208153202.png) #### 2.2.2 Configure the Audit Form Actions ##### 2.2.2.1 Add “Approve” and “Reject” Actions In the application information form, add two actions: “Approve” and “Reject.” ![](https://static-docs.nocobase.com/20250208153302.png) ##### 2.2.2.2 Configure Action Functions * **Configure the “Approve” Action** * Bind it to the workflow just created. * Upon submission, set the **Status** field to “Approved.” Refer to the images: ![](https://static-docs.nocobase.com/20250208153429.png)![](https://static-docs.nocobase.com/20250208153409.png) * **Configure the “Reject” Action** * Upon submission, set the **Status** field to “Rejected.” ##### 2.2.2.3 Set Action Linkage Rules To prevent duplicate operations, set a linkage rule: hide the actions when the **Status** is not “Pending.” Refer to the image: ![](https://static-docs.nocobase.com/20250208153749.png) ### 2.3 Activate and Configure the [Public Form](https://docs.nocobase.com/handbook/public-forms) Plugin Utilize the [Public Form](https://docs.nocobase.com/handbook/public-forms) plugin to allow users to submit their registration applications through a webpage. #### 2.3.1 Activate the [Public Form](https://docs.nocobase.com/handbook/public-forms) Plugin ##### 2.3.1.1 Activate the Plugin * **Access Plugin Management** In the backend management interface, locate and activate the “Public Form” plugin. Refer to the image: ![](https://static-docs.nocobase.com/20250208154258.png) #### 2.3.2 Create and Configure the Public Form ##### 2.3.2.1 Create a Public Form * **Create a New Form** In the backend management, create a [Public Form](https://docs.nocobase.com/handbook/public-forms) for users to submit their registration applications. * **Configure Form Elements** Add the necessary form elements (such as username, email, contact number, etc.) and set the corresponding validation rules. Refer to the image: ![](https://static-docs.nocobase.com/20250208155044.png) #### 2.3.3 Activate and Configure the [Public Form](https://docs.nocobase.com/handbook/public-forms) Plugin (Core) ##### 2.3.3.1 Test the Public Form * **Open the Frontend Page** Visit the [Public Form](https://docs.nocobase.com/handbook/public-forms) page, fill out the form, and submit the application data. * **Verify the Functionality** Check whether the data is correctly entered into the application information collection and that a new user is automatically created through the workflow upon audit approval. Refer to the test result: ![](https://static-docs.nocobase.com/202502191633-register2.gif) ### 2.4 Further Extensions (Optional) After completing the basic registration and audit process, you can extend additional functionalities as needed: #### 2.4.1 Invitation Code Registration * **Function Description**: Limit the range and number of registered users by setting up an invitation code. * **Configuration Idea**: Add an invitation code field to the application form and use a pre-operation event to validate and intercept the field before submission. #### 2.4.2 Automatic Email Notifications * **Function Description**: Automatically send emails for audit results, successful registrations, etc. * **Configuration Idea**: Combine NocoBase's email node to add email-sending operations in the workflow. --- If you encounter any issues during the operation, feel free to visit the [NocoBase community](https://forum.nocobase.com) or refer to the [official documentation](https://docs.nocobase.com). We hope this guide helps you successfully implement user registration auditing based on your actual needs and provides flexibility for further extensions. Wishing you smooth usage and project success! --- url: /tutorials/v1/15-crm-lead-conversion.md --- # Implementing CRM Lead Conversion ## 1. Introduction This tutorial will guide you step-by-step on how to implement the CRM Opportunity Conversion feature in NocoBase. We will cover how to create the necessary collections, configure data management pages, design the conversion process, and set up association management to help you build the entire business process successfully. 🎉 [NocoBase CRM Solution is Now Live — Ready for You to Explore](https://www.nocobase.com/en/blog/crm-solution) ## 2. Preparation: Creating the Required Collections Before starting, we need to prepare the following four collections and configure the relationships between them. ### 2.1 LEAD Collection (Lead) This collection is used to store potential customer information. Its field definitions are as follows: | Field Name | Display Name | Field Interface | Description | | -------------- | ------------------ | ---------------- | --------------------------------------------------------------------------------------------------------- | | id | **Id** | Integer | Primary key | | account_id | **account_id** | Integer | Foreign key to ACCOUNT | | contact_id | **contact_id** | Integer | Foreign key to CONTACT | | opportunity_id | **opportunity_id** | Integer | Foreign key to OPPORTUNITY | | name | **Lead Name** | Single line text | Name of the potential customer | | company | **Company Name** | Single line text | Name of the potential customer's company | | email | **Email** | Email | Email address of the potential customer | | phone | **Phone** | Phone | Contact phone number | | status | **Status** | Single select | Current status of the lead (Not Qualified, New Lead, Processing, Following Up, In Transaction, Completed) | | Account | **Company** | Many to one | Associated to the Company Collection | | Contact | **Contact** | Many to one | Associated to the Contact Collection | | Opportunity | **Opportunity** | Many to one | Associated to the Opportunity Collection | ### 2.2 ACCOUNT Collection (Company) This collection is used to store detailed information about companies. Its field configuration is as follows: | Field Name | Display Name | Field Interface | Description | | ---------- | ------------ | ---------------- | ------------------------------------------- | | name | **Name** | Single line text | Account name (company or organization name) | | industry | **Industry** | Single select | Industry of the account | | phone | **Phone** | Phone | Contact phone number of the account | | website | **Website** | URL | Official website URL of the account | ### 2.3 CONTACT Collection (Contact) This collection stores contact information and includes the following fields: | Field Name | Display Name | Field Interface | Description | | ---------- | ------------ | ---------------- | ---------------------------- | | name | **Name** | Single line text | Name of the contact | | email | **Email** | Email | Email address of the contact | | phone | **Phone** | Phone | Phone number of the contact | ### 2.4 OPPORTUNITY Collection (Opportunity) This collection is used to record opportunity information. Its field definitions are as follows: | Field Name | Display Name | Field Interface | Description | | ---------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------- | | name | **Name** | Single line text | Name of the opportunity | | stage | **Stage** | Single select | Stage of the opportunity (Qualification, Requirement, Proposal, Negotiation, Deal Closed, Successful, Failed) | | amount | **Amount** | Number | Amount of the opportunity | | close_date | **Close Date** | Datetime | Expected close date of the opportunity | ## 3. Understanding the Opportunity Conversion Process ### 3.1 Overview of the Normal Conversion Process An opportunity, when converted from a lead, generally goes through the following process: ![20250225211802](https://static-docs.nocobase.com/20250225211802.png) ### 3.2 Explanation of the Relationships Assuming you have successfully created the above four collections and configured the mapping relationships among them: ![Relationships](https://static-docs.nocobase.com/20250225090913.png) ## 4. Creating Data Management Pages In the NocoBase workspace, create data management pages for each collection and add some sample lead data for subsequent testing. ![Data Management Page](https://static-docs.nocobase.com/20250224234721.png) ## 5. Implementing the Opportunity Conversion Function This section focuses on explaining how to convert a lead into company, contact, and opportunity records, and ensuring that the conversion operation is not triggered repeatedly. ### 5.1 Create the "Conversion" Edit Operation In the lead detail view, create an edit operation named "Conversion". In the conversion modal, configure the following: #### 5.1.1 Display Lead Basic Information Display the basic information of the current lead in read-only mode to ensure that users do not accidentally modify the original data. #### 5.1.2 Display Associated Relationship Fields In the modal, display the following three association fields and enable the "Quick Create" feature for each. This allows immediate creation of new data if no matching record is found. ![Display Associated Fields](https://static-docs.nocobase.com/20250224234155.png) #### 5.1.3 Configure the Default Mapping for Quick Create In the "Quick Create" modal settings, configure default values for each association field so that the lead information is automatically mapped to the target collection. The mapping rules are as follows: - Lead/Lead Name → Company/Name - Lead/Email → Company/Email - Lead/Phone → Company/Phone - Lead/Lead Name → Contact/Name - Lead/Email → Contact/Email - Lead/Phone → Contact/Phone - Lead/Lead Name → Opportunity/Name - Lead/Status → Opportunity/Stage Configuration example screenshots: ![Default Mapping 1](https://static-docs.nocobase.com/20250225000218.png) ![Default Mapping 2](https://static-docs.nocobase.com/20250225001256.png) #### 5.1.4 Viewing the Conversion Effect After completing the configuration, when the conversion operation is executed, the system will create and associate new company, contact, and opportunity records based on the mapping rules. The effect is demonstrated below: ![](https://static-docs.nocobase.com/202502252130-transfer1.gif) Next, we will add a success feedback for the submission operation: ![20250226154935](https://static-docs.nocobase.com/20250226154935.png) ![20250226154952](https://static-docs.nocobase.com/20250226154952.png) Submission effect: ![](https://static-docs.nocobase.com/202502252130-transfer2.gif) ### 5.2 Preventing Duplicate Conversions To avoid converting the same lead multiple times, you can control it in the following ways: #### 5.2.1 Update Lead Status In the conversion form submission operation, add an automatic data update step to change the lead status to "Converted". Configuration screenshots: ![Update Status 1](https://static-docs.nocobase.com/20250225001758.png) ![Update Status 2](https://static-docs.nocobase.com/20250225001817.png) Effect demonstration: ![Conversion Effect](https://static-docs.nocobase.com/202502252130-transfer.gif) #### 5.2.2 Set Button Linking Rules Add linking rules to the conversion button: when the lead status is "Converted", automatically hide the conversion button to prevent duplicate operations. Configuration screenshots: ![Button Linking 1](https://static-docs.nocobase.com/20250225001838.png) ![Button Linking 2](https://static-docs.nocobase.com/20250225001939.png) ![Button Linking 3](https://static-docs.nocobase.com/20250225002026.png) ## 6. Configuring the Association Management Blocks on the Detail Pages To allow users to view associated data on the detail pages of each Collection, you need to configure the corresponding list blocks or detail blocks. ### 6.1 Configure the Company Collection Detail Page In the company detail page (for example, within the contact's edit/detail modal), add the following list blocks: - Contact list block - Opportunity list block - Lead list block Example screenshot: ![Company Detail Page](https://static-docs.nocobase.com/20250225085418.png) ### 6.2 Add Filter Conditions For each list block, add filter rules to ensure that only data associated with the current company ID is displayed. Configuration screenshots: ![Filter Condition 1](https://static-docs.nocobase.com/20250225085513.png) ![Filter Condition 2](https://static-docs.nocobase.com/20250225085638.png) ### 6.3 Configure the Contact and Opportunity Detail Pages In the detail modal of the Contact Collection, add the following blocks: - Opportunity list block - Company detail block - Lead list block (filtered by ID) Screenshot: ![Contact Detail](https://static-docs.nocobase.com/20250225090231.png) In the Opportunity detail page, similarly add: - Contact list block - Company detail block - Lead list block (filtered by ID) Screenshot: ![Opportunity Detail](https://static-docs.nocobase.com/20250225091208.png) ## 7. Conclusion Through the steps above, you have successfully implemented a simple CRM opportunity conversion feature and configured the association management between contacts, companies, and leads. We hope this tutorial provides a clear, step-by-step guide to help you master the construction of the entire business process, bringing convenience and efficient operations to your project. --- If you encounter any issues during the operation, feel free to visit the [NocoBase community](https://forum.nocobase.com) or refer to the [official documentation](https://docs.nocobase.com). We hope this guide helps you successfully implement user registration auditing based on your actual needs and provides flexibility for further extensions. Wishing you smooth usage and project success! --- url: /tutorials/v1/16-markdown-tips.md --- # Markdown Blocks Tips and Tricks Markdown Block is one of the most commonly used and powerful blocks in our system. It can range from lightweight text prompts to simple HTML styling, and can even handle essential business logic—with versatile and flexible functionality. ## 1. Basic Functions of the Markdown Block Due to the flexible, open, and easily modifiable nature of the Markdown block, it is often used to display system announcements. Whether it is a business module, a feature, a block, or even a field, we can post little tips just like using sticky notes. Before using the Markdown block, it is recommended that you first familiarize yourself with Markdown formatting and syntax. You can refer to the [Vditor Example](https://docs.nocobase.com/api/field/markdown-vditor). > Note: The Markdown block on the page is relatively lightweight, and certain features (such as mathematical formulas, mind maps, etc.) are currently not supported for rendering. However, we can implement them using HTML; the system also provides a Vditor field component for you to try. ### 1.1 Page Examples You can observe the use of Markdown on the system’s “Online Demo” page. Specific examples can be found on the home page, the order page, and in the “More Examples” section. For instance, the warnings and tips on our homepage: ![20250227085425](https://static-docs.nocobase.com/20250227085425.png) The calculation logic in the order module: ![20250227085536](https://static-docs.nocobase.com/20250227085536.png) Guidance and images in the “More Examples” section: ![20250227085730](https://static-docs.nocobase.com/20250227085730.png) By switching to edit mode, you can modify the Markdown content at any time and observe the changes on the page. ![20250227085855](https://static-docs.nocobase.com/20250227085855.png) ### 1.2 Creating a Markdown Block You can flexibly create Markdown blocks in pages, pop-ups, and forms. #### 1.2.1 Creation Methods - **Creating in Pop-ups/Pages:** ![Markdown Block in Pop-ups/Pages](https://static-docs.nocobase.com/20250227091156.png) - **Creating in Form Blocks:** ![Markdown Block in Form Blocks](https://static-docs.nocobase.com/20250227091309.png) #### 1.2.2 Usage Examples By typing `---` using Markdown syntax, you can simulate a horizontal grouping line to achieve simple content separation, as demonstrated below: ![Separator Example 1](https://static-docs.nocobase.com/20250227092156.png) ![Separator Example 2](https://static-docs.nocobase.com/20250227092236.png) --- ## 2. Personalized Content Display Another major advantage of Markdown blocks is that they support system variable interpolation, which helps generate personalized titles and tip messages—ensuring that each user sees a unique display within their form. ![Personalized Display 1](https://static-docs.nocobase.com/20250227092400.png) ![Personalized Display 2](https://static-docs.nocobase.com/20250227092430.png) In addition, you can combine form data to perform simple content formatting, as illustrated in the following example: **Highlighted Title Example:** ```markdown # #{{$nRecord.id}} {{$nPopupRecord.task_name}} --- ``` ![Highlighted Title Effect](https://static-docs.nocobase.com/20250227164055.png) **Centered Divider Example:** ![Centered Divider Effect](https://static-docs.nocobase.com/20250227164456.png) ## 3. Enriching Content with Additional Elements As you become more familiar with Markdown syntax and variables, you can also enrich Markdown blocks with additional content, such as HTML! ### 3.1 HTML Example If you are not familiar with HTML syntax, you can let Deepseek help write it (note that the `script` tag is not supported; it is recommended that all styles be written within a local `div`). Below is an elegant announcement example: ```html

Join Us for a Fun Getaway!

Hi Everyone,

We're excited to invite you to an awesome group outing filled with laughter, adventure, and great vibes!

Mark your calendars for November 10, 2025, and get ready to explore, relax, and enjoy some quality time together.

We'll share more details about the itinerary and meeting spot soon—stay tuned!

Can't wait to see you there!

Cheers,

Your Event Team

``` ![20250227092832](https://static-docs.nocobase.com/20250227092832.png) ![20250227093003](https://static-docs.nocobase.com/20250227093003.png) ### 3.2 Animation Effect Example We can even combine CSS to implement simple animation effects—similar to a slideshow that dynamically shows and hides content. Try pasting the following code into your Markdown to see it in action! ```html

🎉 Special Announcement 🎉

Thank you for your support and attention! We will hold a special event next Monday, stay tuned!

``` ![](https://static-docs.nocobase.com/202502270933fade-out.gif) --- url: /tutorials/v1/17-crm-overview.md --- # CRM Sales Cloud Overview In this chapter, the system is divided into multiple modules based on business functions. Each module's core functionalities and corresponding data structures are described in detail. This solution not only prioritizes smooth business processes but also considers rational data storage and system scalability. --- ## 1. Lead Management ### Function Overview The Lead Management module is responsible for capturing and managing prospective customer information. The system supports capturing leads through various channels—such as websites, telephone, and email—and provides functionalities for status updates, follow-up tracking, and adding remarks. During the lead conversion process, the system automatically checks for duplicate entries to ensure that appropriate leads are transformed into customers, contacts, and opportunities. ### Relevant Data Tables - **Leads (Lead Table)** Stores basic lead information, including name, contact details, source, current status, and remarks. It also records the creation time and update logs for each lead to facilitate later analysis and statistics. --- ## 2. Account & Contact Management ### Function Overview This module is designed to help users build and maintain customer profiles. Enterprises can record key information such as company name, industry, address, and other crucial details, while also managing related contact information (e.g., name, position, phone number, and email). The system supports one-to-many or many-to-many relationships between customers and contacts, ensuring complete and synchronized data. ### Relevant Data Tables - **Accounts (Customer Table)** Stores detailed customer profiles, including basic company information and other business-related data. - **Contacts (Contact Table)** Stores personal information associated with customers and establishes a foreign key relationship with the customer table to ensure data consistency. ### Lead Conversion Flowchart ![20250225211802](https://static-docs.nocobase.com/20250225211802.png) - Lead capture → Lead follow-up (status update) → Lead verification → Conversion into accounts, contacts, and opportunities --- ## 3. Opportunity Management ### Function Overview The Opportunity Management module focuses on converting leads or existing customer information into sales opportunities. Users can record details such as the expected closing date, current stage, estimated amount, and success probability. The system supports dynamic management of sales stages and records detailed reasons when an opportunity fails, facilitating future sales strategy optimization. Additionally, the module allows multiple products to be associated with a single opportunity, automatically calculating the total amount. ### Relevant Data Tables - **Opportunities (Opportunity Table)** Records detailed information for each sales opportunity, such as closing dates, sales stages, and estimated amounts. - **OpportunityLineItem (Opportunity Line Item Table)** Stores specific product details related to opportunities, including product ID, quantity, unit price, and discount, with support for automatic amount calculation. ### Conversion Steps - Opportunity creation → Opportunity management (stage update) → Quote generation → Customer approval → Sales order creation → Order execution and status update --- ## 4. Product & Price Book Management ### Function Overview This module is responsible for managing product information and pricing strategies. The system can capture basic product details such as product code, name, description, inventory, and price, and supports the establishment of multiple pricing models. By associating products with price books, users can flexibly manage pricing requirements for different markets and customer segments. ### Relevant Data Tables - **Products (Product Table)** Stores detailed information for all products, providing foundational data for generating quotes and orders. - **PriceBooks (Price Book Table)** Manages various pricing models and their associated products, supporting dynamic adjustments to pricing strategies based on business needs. --- ## 5. Quote Management ### Function Overview The Quote Management module generates formal quotes from existing opportunities, recording details such as the validity period, discounts, tax rates, and total amounts. The system incorporates an internal approval process, allowing management to review and adjust quotes; each quote may include multiple product line items to ensure accurate calculations. ### Relevant Data Tables - **Quotes (Quote Table)** Records basic information for quotes, including the associated opportunity, validity period, discount, tax rate, and overall status. - **QuoteLineItems (Quote Line Item Table)** Stores detailed data for each product line item in a quote, automatically calculating individual product amounts as well as the total quote amount. --- ## 6. Sales Order Management ### Function Overview The Sales Order Management module converts approved quotes into sales orders and tracks the entire lifecycle from creation to completion. Users can view order status, approval records, as well as logistics and shipping details in real time, thereby better managing the order execution process. ### Relevant Data Tables - **SalesOrders (Sales Order Table)** Records detailed information for sales orders, including the associated quote, order status, approval records, shipping status, and creation time. --- ## 7. Activity Management ### Function Overview The Activity Management module assists the sales team in managing daily schedules, including tasks, meetings, and phone calls. The system allows the recording of activity details, participants, and related remarks, and provides scheduling and reminder functionalities to ensure that all activities proceed as planned. ### Relevant Data Tables - **Activities (Activity Record Table)** Stores records for tasks, meetings, and calls, including activity type, date, participants, and related customer or opportunity information. --- ## 8. Data Reporting and Analytics ### Function Overview This module leverages multidimensional data analysis and graphical presentations to help enterprises gain real-time insights into sales performance and business conversions. The system supports the generation of sales funnels, conversion rate analysis, and performance reports, thereby providing decision-making support for management. ### Note Although there is no dedicated data table for reporting and analytics, this module relies on the data stored across the aforementioned modules, enabling real-time feedback and trend forecasting through data aggregation and analysis. --- ## 9. Marketing Campaign Management (Optional Module) ### Function Overview As an auxiliary function, the Marketing Campaign Management module is primarily used for planning and tracking marketing activities. The system can record campaign planning, budgeting, execution processes, and performance evaluations, tracking lead conversion rates and return on investment (ROI) to provide data support for marketing initiatives. ### Note The data structure for this module can be expanded based on actual requirements; currently, it primarily records the execution details of marketing campaigns, complementing the data from the Lead Management module. --- url: /tutorials/v1/18-lead-followup.md --- # Lead Follow-up & Status Management ## 1. Introduction ### 1.1 Chapter Objective In this chapter, we will learn how to implement CRM lead conversion in NocoBase. Through lead follow-up and status management, you can boost operational efficiency and achieve more refined sales process control. ### 1.2 Preview of the Final Outcome In the previous chapter, we explained how to associate data between leads and the Company, Contact, and Opportunity collections. Now, we focus on the Lead module, primarily discussing how to perform lead follow-up and status management. Please watch the following demo: ![](https://static-docs.nocobase.com/202502250226-transfer3.gif) ## 2. Structure of the Lead Collection ### 2.1 Introduction to the Lead Collection In the lead follow-up functionality, the "status" field plays a crucial role. It not only reflects the current progress of the lead (e.g., Unqualified, New, Working, Nurturing, In Transaction, Completed) but also drives the dynamic display and changes of the form. The following table block shows the field structure of the Lead collection along with its detailed description: | Field name | Display Name | Field Interface | Description | | -------------- | ------------------ | ---------------- | ------------------------------------------------------------------------------------- | | id | **Id** | Integer | Primary key | | account_id | **account_id** | Integer | Foreign key of the ACCOUNT collection | | contact_id | **contact_id** | Integer | Foreign key of the CONTACT collection | | opportunity_id | **opportunity_id** | Integer | Foreign key of the OPPORTUNITY collection | | name | **Lead Name** | Single line text | Name of the prospective customer | | company | **Company Name** | Single line text | Name of the prospective customer's company | | email | **Email** | Email | Email address of the prospective customer | | phone | **Contact Number** | Phone | Contact number | | status | **Status** | Single select | Current lead status (Unqualified, New, Working, Nurturing, In Transaction, Completed) | | Account | **Company** | Many to one | Linked to the Company collection | | Contact | **Contact** | Many to one | Linked to the Contact collection | | Opportunity | **Opportunity** | Many to one | Linked to the Opportunity collection | ## 3. Creating the Leads Table Block and Detail Block ### 3.1 Instructions for Creation First, we need to create a "Leads" table block to display the necessary fields. At the same time, configure a detail block on the right side of the page so that when you click on a record, the corresponding details are automatically displayed. Please refer to the demo below: ![](https://static-docs.nocobase.com/20250226090009.png) ## 4. Configuring Action Buttons ### 4.1 Overall Description of the Buttons To meet various operational needs, we need to create a total of 11 buttons. Each button will display differently (hidden, active, or disabled) based on the record's status, guiding the user through the correct business process. ![](https://static-docs.nocobase.com/20250226173632.png) ### 4.2 Detailed Configuration for Each Function Button #### 4.2.1 Edit Button - Linkage Rule: When the record's status is "Completed", this button is automatically disabled to prevent unnecessary editing. #### 4.2.2 Unqualified Button 1 (Inactive) - Appearance: The title is displayed as "Unqualified >". - Operation: On click, execute an update operation that sets the record's status to "Unqualified". Upon successful update, return to the previous page and display a success message for "Unqualified". - Linkage Rule: Displayed only when the record's status is empty; once a status is set, the button is automatically hidden. #### 4.2.3 Unqualified Button 2 (Active) - Appearance: Also displayed as "Unqualified >". - Operation: Used to update the record's status to "Unqualified". - Linkage Rule: Hidden when the status is empty; if the status is "Completed", the button is disabled. #### 4.2.4 New Button 1 (Inactive) - Appearance: The title is displayed as "New >". - Operation: On click, update the record by setting the status to "New" and, upon success, display a "New" success message. - Linkage Rule: If the record's status is already "New", "Working", "Nurturing", or "Completed", the button is hidden. #### 4.2.5 New Button 2 (Active) - Appearance: The title remains "New >". - Operation: Also used to update the record's status to "New". - Linkage Rule: Hidden when the status is "Unqualified" or empty; if the status is "Completed", the button is disabled. #### 4.2.6 Working Button (Inactive) - Appearance: The title is displayed as "Working >". - Operation: On click, update the record's status to "Working" and display a "Working" success message. - Linkage Rule: If the record's status is already "Working", "Nurturing", or "Completed", the button is hidden. #### 4.2.7 Working Button (Active) - Appearance: The title remains "Working >". - Operation: Used to update the record's status to "Working". - Linkage Rule: Hidden when the status is "Unqualified", "New", or empty; if the status is "Completed", the button is disabled. #### 4.2.8 Nurturing Button (Inactive) - Appearance: The title is displayed as "Nurturing >". - Operation: On click, update the record's status to "Nurturing" and display a "Nurturing" success message. - Linkage Rule: If the record's status is already "Nurturing" or "Completed", the button is hidden. #### 4.2.9 Nurturing Button (Active) - Appearance: The title remains "Nurturing >". - Operation: Also used to update the record's status to "Nurturing". - Linkage Rule: Hidden when the status is "Unqualified", "New", "Working", or empty; if the status is "Completed", the button is disabled. #### 4.2.10 Transfer Button - Appearance: The title is displayed as "transfer" and opens in a modal window. - Operation: Mainly used to execute the record transfer operation. After the update, the system will display an interface with a drawer, tabs, and a form to facilitate the transfer. - Linkage Rule: When the record's status is "Completed", this button is hidden to prevent duplicate transfers. ![](https://static-docs.nocobase.com/20250226094223.png) #### 4.2.11 Transferred Button (Active) - Appearance: The title is displayed as "transfered" and also opens in a modal window. - Operation: This button is only used to display information after the transfer is completed and does not allow editing. - Linkage Rule: Displayed only when the record's status is "Completed"; it is hidden for statuses such as "Unqualified", "New", "Working", "Nurturing", or when empty. ![](https://static-docs.nocobase.com/20250226094203.png) ### 4.3 Summary of Button Configurations - Each function provides different button styles for inactive and active states. - Linkage rules dynamically control the display (hidden or disabled) of the buttons based on the record's status, guiding sales personnel to follow the correct workflow. ## 5. Form Linkage Rule Settings ### 5.1 Rule 1: Display Only the Name - When the record is not confirmed or the status is empty, only the name is displayed. ![](https://static-docs.nocobase.com/20250226092629.png) ![](https://static-docs.nocobase.com/20250226092555.png) ### 5.2 Rule 2: Optimized Display Under "New" Status - When the status is "New", the company name is hidden and the contact information is displayed. ![](https://static-docs.nocobase.com/20250226092726.png) ## 6. Page Markdown Rules and Handlebars Syntax ### 6.1 Dynamic Text Display In the page, we use Handlebars syntax to dynamically display different prompt messages based on the record's status. Below are example codes for each status: When the status is "Unqualified": ```markdown {{#if (eq $nRecord.status "Unqualified")}} **Track the details of your unqualified leads.** If your lead is not interested in the product or has left the related company, it might be unqualified. - Record lessons learned for future reference - Save outreach details and contact information {{/if}} ``` When the status is "New": ```markdown {{#if (eq $nRecord.status "New")}} **Identify the products or services required for this opportunity.** - Gather customer cases, reference materials, or competitor analyses - Confirm your key stakeholders - Determine available resources {{/if}} ``` When the status is "Working": ```markdown {{#if (eq $nRecord.status "Working")}} **Deliver your solution to stakeholders.** - Communicate the value of your solution - Clarify timelines and budgets - Develop a plan with the customer on when and how to close the deal {{/if}} ``` When the status is "Nurturing": ```markdown {{#if (eq $nRecord.status "Nurturing")}} **Determine the customer's project implementation plan.** - Reach agreements as needed - Follow the internal discount process - Obtain a signed contract {{/if}} ``` When the status is "Completed": ```markdown {{#if (eq $nRecord.status "Completed")}} **Confirm the project implementation plan and final steps.** - Ensure all remaining agreements and sign-off procedures are in place - Adhere to the internal discount policy - Ensure the contract is signed and the project proceeds as planned {{/if}} ``` ## 7. Displaying Associated Objects and Jump Links After Conversion ### 7.1 Description of Associated Objects After conversion, we want to display the associated objects (Company, Contact, Opportunity) along with links to their detail pages. Note: In other pop-ups or pages, the last part of the detail link (after filterbytk) represents the current object's id. For example: ```text http://localhost:13000/apps/tsting/admin/w3yyu23uro0/popups/ki0wcnfruj6/filterbytk/1 ``` ### 7.2 Generating Associated Links Using Handlebars For Company: ```markdown {{#if (eq $nRecord.status "Completed")}} **Account:** [{{$nRecord.account.name}}](http://localhost:13000/apps/tsting/admin/w3yyu23uro0/popups/ki0wcnfruj6/filterbytk/{{$nRecord.account_id}}) {{/if}} ``` For Contact: ```markdown {{#if (eq $nRecord.status "Completed")}} **Contact:** [{{$nRecord.contact.name}}](http://localhost:13000/apps/tsting/admin/1oqybfwrocb/popups/8bbsqy5bbpl/filterbytk/{{$nRecord.contact_id}}) {{/if}} ``` For Opportunity: ```markdown {{#if (eq $nRecord.status "Completed")}} **Opportunity:** [{{$nRecord.opportunity.name}}](http://localhost:13000/apps/tsting/admin/si0io9rt6q6/popups/yyx8uflsowr/filterbytk/{{$nRecord.opportunity_id}}) {{/if}} ``` ![](https://static-docs.nocobase.com/20250226093501.png) ## 8. Hiding Associated Objects but Retaining Their Values To ensure that associated information is displayed properly after conversion, the statuses for "Company", "Contact", and "Opportunity" should be set to "hidden (retain value)". This way, even though these fields are not shown on the form, their values are still recorded and passed along. ![](https://static-docs.nocobase.com/20250226093906.png) ## 9. Preventing Status Modification After Conversion To prevent accidental changes to the status after conversion, we add a condition to all buttons: when the status is "Completed", all buttons will be disabled. ![](https://static-docs.nocobase.com/20250226094302.png) ## 10. Conclusion After completing all of the above steps, your lead follow-up conversion functionality is complete! Through this step-by-step guide, we hope you have gained a clearer understanding of how status-based form dynamics and linkages are implemented in NocoBase. Wishing you smooth operations and an enjoyable experience! ``` --- url: /tutorials/v1/19-crm-sales-cloud.md --- # CRM System (Sales Cloud) ## 1.1 Background - **Background Description**With the rapid pace of digital transformation, establishing a unified sales platform to manage leads, accounts, opportunities, quotes, orders, and daily activities is essential. This solution, built on NocoBase, delivers a CRM system (Sales Cloud) that centralizes data management and streamlines intelligent workflows. > Note: This proposal represents the first phase of a lightweight CRM system. It does not include procurement, shipping, or vendor management functionalities and is intended primarily as a demonstration of the functional implementation. > ## 1.2 Project Objectives and Scope - **Objectives** Develop an integrated CRM system that consolidates the management of leads, accounts, contacts, opportunities, products, quotes, orders, and activities to enhance sales efficiency and ensure data accuracy. - **Scope**Cover the entire sales process—from data entry and tracking to conversion and analytical reporting. Marketing campaign management is offered as an optional module, while procurement, shipping, and vendor management are currently excluded. ## 1.3 Overview of User Roles and Permissions - **Sales Representative**: Responsible for entering and following up on leads, creating accounts, contacts, opportunities, quotes, and orders, and recording daily activities. - **Sales Manager**: Monitors sales progress, approves quotes and orders, and reviews various performance reports. - **System Administrator**: Manages user permissions, maintains data integrity, configures system settings, and oversees interface integrations. ## 1.4 Overview of Functional Modules Below is a concise table summarizing the core modules and sub-modules, offering a quick insight into the system’s key functionalities: | Functional Module | Sub-modules/Features | Description | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | | **Lead Management** | Lead Entry, Follow-Up & Status Management, Lead Conversion | Capture and follow up on prospective customer information, converting qualified leads into accounts, contacts, and opportunities. | | **Account & Contact Management** | Account Management, Contact Management | Establish account profiles by recording company and contact details. | | **Opportunity Management** | Opportunity Creation, Sales Stage Management, Forecast Analysis, Product Management, Close Reason, Expected Payment Management | Record sales opportunities, track the sales process, and forecast trends. | | **Product & Price Book Management** | Product Catalog Management, Price Book Management | Manage product details, inventory, and pricing information. | | **Quote Management** | Quote Generation, Approval Process, Quote Line Item Management | Generate and manage official quotes with support for approvals and automatic total calculations. | | **Sales Order Management** | Order Creation, Status Tracking, Order Shipment Management | Document the creation, approval, and logistics status of orders. | | **Activity Management** | Task Management, Meeting Records, Call Records, Calendar & Reminders | Manage sales tasks, meetings, and calls, with scheduling and reminder functionalities. | | **Reports & Analytics** | Sales Performance Reports, Opportunity Conversion Analysis, Sales Funnel Analysis | Generate multidimensional sales reports to support comprehensive data analysis. | | **Marketing Campaign Management (Optional)** | Marketing Campaign Planning, Campaign Performance Tracking | Plan and evaluate marketing campaigns. | --- Continuously updated... --- url: /tutorials/v1/20-api-keys.md --- # Using API Keys to Retrieve Data Dear friends, welcome to this tutorial. In this document, I will guide you step-by-step on how to use API Keys in NocoBase to retrieve data, using the "To Dos" example to help you understand every detail. Please read the following content carefully and follow the instructions. ![202503032004-todo1](https://static-docs.nocobase.com/202503032004-todo1.gif) ## 1 Understanding the Concept of API Keys Before we begin, it's important to understand: What is an API Key? It works like an entry ticket that verifies whether an API request comes from an authorized user. When you access the NocoBase system via a website, mobile app, or backend script, this "secret key" quickly validates your identity. In the HTTP request header, you'll see a format like: ```txt Authorization: Bearer {API key} ``` Here, "Bearer" indicates that what follows is a validated API Key, which allows the system to quickly confirm the requester's permissions. In practice, API Keys are commonly used in the following scenarios: 1. **Client-side Application Access**: When users access the API through a web browser or mobile app, the system uses the API Key to verify the user's identity, ensuring that only authorized users can retrieve data. 2. **Automated Task Execution**: Background scheduled tasks or scripts use API Keys to ensure that updates or logging actions are secure and authorized. 3. **Development and Testing**: Developers use API Keys during debugging and testing to simulate real requests and ensure that the interfaces respond correctly. In short, API Keys not only help confirm the identity of the requester but also monitor usage, limit request frequency, and prevent potential security threats, thereby safeguarding the stable operation of NocoBase. ## 2 Creating [API Keys](https://docs.nocobase.com/handbook/api-keys) in NocoBase ### 2.1 Activate the Auth: [API Keys](https://docs.nocobase.com/handbook/api-keys) Plugin First, make sure that the built-in Auth: [API Keys](https://docs.nocobase.com/handbook/api-keys) plugin in NocoBase is activated. Once activated, the system settings center will feature a new configuration page for [API keys](https://docs.nocobase.com/handbook/api-keys). ![20250301003106](https://static-docs.nocobase.com/20250301003106.png) ### 2.2 Create a Test To Dos Collection For testing purposes, create a collection named `todos` (To Dos collection) with the following fields: - `id` - `title` - `completed` ![20250303175632](https://static-docs.nocobase.com/20250303175632.png) Then, enter a few sample to-dos into the collection, such as: - eat food - sleep - play games ![20250303180044](https://static-docs.nocobase.com/20250303180044.png) ### 2.3 Create and Bind a Role Since [API keys](https://docs.nocobase.com/handbook/api-keys) are bound to user roles, the system determines request permissions based on the role. Therefore, before creating an [API key](https://docs.nocobase.com/handbook/api-keys), you need to create a role and assign the appropriate permissions. It is recommended to create a test role called "To Dos API Role" and assign all permissions for the To Dos collection to this role. ![20250303180247](https://static-docs.nocobase.com/20250303180247.png) If "To Dos API Role" is not available when creating an [API key](https://docs.nocobase.com/handbook/api-keys), it may be because the current user has not been assigned this role. In that case, please assign this role to the current user: ![20250303180638](https://static-docs.nocobase.com/20250303180638.png) After assigning the role, refresh the page and navigate to the API key management page. Click "Add API Key," and you should see the "To Dos API Role" role available. ![20250303180612](https://static-docs.nocobase.com/20250303180612.png) ![20250303180936](https://static-docs.nocobase.com/20250303180936.png) For more precise management, you can also create a dedicated "To Dos API User" to log into the system, test permissions, and manage [API keys](https://docs.nocobase.com/handbook/api-keys). Simply assign the "To Dos API Role" to this user. ![20250304134443](https://static-docs.nocobase.com/20250304134443.png) ![20250304134713](https://static-docs.nocobase.com/20250304134713.png) ![20250304134734](https://static-docs.nocobase.com/20250304134734.png) ### 2.4 Create and Save the [API Key](https://docs.nocobase.com/handbook/api-keys) After clicking submit, the system will display a prompt indicating that the [API key](https://docs.nocobase.com/handbook/api-keys) has been created successfully, and the key will be shown in a popup. Be sure to copy and save this key, as it will not be shown again for security reasons. ![20250303181130](https://static-docs.nocobase.com/20250303181130.png) For example, you might receive an [API key](https://docs.nocobase.com/handbook/api-keys) like this: ```txt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInJvbGVOYW1lIjoidG9kb3MiLCJpYXQiOjE3NDA5OTY1ODAsImV4cCI6MzMyOTg1OTY1ODB9.tXF2FCAzFNgZFPXqSBbWAfEvWkQwz0-mtKnmOTZT-5M ``` ### 2.5 Notes - The validity period of the [API key](https://docs.nocobase.com/handbook/api-keys) depends on the duration you selected when applying. - The generation and verification of the [API key](https://docs.nocobase.com/handbook/api-keys) are closely tied to the `APP_KEY` environment variable. Do not modify it arbitrarily, or all [API keys](https://docs.nocobase.com/handbook/api-keys) in the system will become invalid. ## 3 Testing the [API Key](https://docs.nocobase.com/handbook/api-keys)’s Validity ### 3.1 Using the [API document](https://docs.nocobase.com/handbook/api-doc) Plugin Open the [API document](https://docs.nocobase.com/handbook/api-doc) plugin, where you can view the request method, URL, parameters, and header information for each API. ![20250303181522](https://static-docs.nocobase.com/20250303181522.png) ![20250303181704](https://static-docs.nocobase.com/20250303181704.png) ### 3.2 Understanding the Basic CRUD APIs Below are some basic API examples provided by NocoBase: - **List Query (list API):** ```txt GET {baseURL}/{collectionName}:list Request Header: - Authorization: Bearer ``` - **Create Record (create API):** ```txt POST {baseURL}/{collectionName}:create Request Header: - Authorization: Bearer Request Body (in JSON format), for example: { "title": "123" } ``` - **Update Record (update API):** ```txt POST {baseURL}/{collectionName}:update?filterByTk={id} Request Header: - Authorization: Bearer Request Body (in JSON format), for example: { "title": "123", "completed": true } ``` - **Delete Record (delete API):** ```txt POST {baseURL}/{collectionName}:destroy?filterByTk={id} Request Header: - Authorization: Bearer ``` Here, `{baseURL}` is the URL of your NocoBase system and `{collectionName}` is the collection name. For example, when testing locally, if the address is `localhost:13000` and the collection name is `todos`, the request URL would be: ```txt http://localhost:13000/todos:list ``` ### 3.3 Testing Using Postman (Example with the List API) Open Postman, create a new GET request, enter the request URL above, and add the `Authorization` header with your [API key](https://docs.nocobase.com/handbook/api-keys) as the value: ```txt Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInJvbGVOYW1lIjoidG9kb3MiLCJpYXQiOjE3NDA5OTY1ODAsImV4cCI6MzMyOTg1OTY1ODB9.tXF2FCAzFNgZFPXqSBbWAfEvWkQwz0-mtKnmOTZT-5M ``` ![20250303182744](https://static-docs.nocobase.com/20250303182744.png) After sending the request, if everything is set up correctly, you should receive a response similar to the following: ```json { "data": [ { "createdAt": "2025-03-03T09:57:36.728Z", "updatedAt": "2025-03-03T09:57:36.728Z", "completed": null, "createdById": 1, "id": 1, "title": "eat food", "updatedById": 1 } ], "meta": { "count": 1, "page": 1, "pageSize": 20, "totalPage": 1 } } ``` If the [API key](https://docs.nocobase.com/handbook/api-keys) is not correctly authorized, you might see an error message like: ```json { "errors": [ { "message": "Your session has expired. Please sign in again.", "code": "INVALID_TOKEN" } ] } ``` In such a case, please check the role permissions, [API key](https://docs.nocobase.com/handbook/api-keys) binding, and ensure the key format is correct. ### 3.4 Copy the Request Code from Postman Once the test is successful, you can copy the request code for the List API. For instance, the following curl request example is copied from Postman: ```txt curl --location 'http://localhost:13000/api/todos:list' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInJvbGVOYW1lIjoidG9kb3MiLCJpYXQiOjE3NDA5OTY1ODAsImV4cCI6MzMyOTg1OTY1ODB9.tXF2FCAzFNgZFPXqSBbWAfEvWkQwz0-mtKnmOTZT-5M' ``` ![20250303184912](https://static-docs.nocobase.com/20250303184912.png) ![20250303184953](https://static-docs.nocobase.com/20250303184953.png) ## 4 Displaying To Dos in an [Iframe Block](https://docs.nocobase.com/handbook/block-iframe) To visually demonstrate the effect of API requests, we can use an [Iframe Block](https://docs.nocobase.com/handbook/block-iframe) to display the list of To Dos fetched from NocoBase. Refer to the sample code below: ```html Todo List

Todo List



    


```

The above code embeds an [Iframe Block](https://docs.nocobase.com/handbook/block-iframe) that displays a simple "Todo List". Upon loading, it calls the API to retrieve the To Dos and shows the response (as formatted JSON) within the iframe.

Additionally, the following animation demonstrates the dynamic process of the request:

![202503031918-fetch](https://static-docs.nocobase.com/202503031918-fetch.gif)

## 5 Conclusion

Through the above steps, we have explained in detail how to create and use [API keys](https://docs.nocobase.com/handbook/api-keys) in NocoBase. From activating the plugin, creating a collection, and binding a role, to testing the API and displaying data in an [Iframe Block](https://docs.nocobase.com/handbook/block-iframe)—each step is crucial. Finally, with the help of DeepSeek, a simple To Dos page was created. Feel free to modify and extend the code as needed.

![202503031942-todo](https://static-docs.nocobase.com/202503031942-todo.gif)

[The code for this example](https://forum.nocobase.com/t/todo-list-1-0-how-to-using-api-keys/3315) is available in our community post. We welcome your feedback and discussion. We hope this document provides you with clear guidance, and we wish you happy learning and smooth operations!



---
url: /tutorials/v1/21-markdown-variables.md
---

# Markdown Template Variables

Dear guys, welcome to this tutorial! In this section, we will learn step-by-step how to use Markdown together with the Handlebars templating engine to achieve dynamic content display. In the previous lesson, "The Marvels of Markdown Blocks," you learned about the basic syntax, creation methods, and variable filling. Now, let’s dive into some advanced techniques with template variables.

## 1 Introduction to the [Handlebars](https://docs.nocobase.com/handbook/template-handlebars) Templating Engine

After you create a Markdown block, you will find a "Templating Engine" option in the top-right configuration panel, with Handlebars set as the default. Handlebars helps you dynamically render page content based on conditions (such as statuses, numerical values, or options), enabling Markdown to respond to changes.

![Templating Engine Diagram](https://static-docs.nocobase.com/20250304011925.png)

### 1.1 The Role of Handlebars

Although Markdown natively only supports static content, by using Handlebars you can toggle display text and styles dynamically based on conditions. This way, even in varying business scenarios, your page can always display the correct information.

## 2 Practical Scenarios

Let’s explore some practical scenarios and implement their functionalities step by step.

#### 2.1 Handling Order Status

In an online demo, we often need to display different messages depending on the order status. Suppose your orders have a status field with the following statuses:

![Order Status Field](https://static-docs.nocobase.com/20250304091420.png)

The display contents for the four statuses are as follows:


| Option Label     | Option Value | Display Content                                                                                                        |
| ---------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------- |
| Pending Approval | 1            | Order submitted, awaiting internal review.                                                                             |
| Pending Payment  | 2            | Awaiting customer payment. Please closely monitor the order status.                                                    |
| Paid             | 3            | Payment confirmed; please proceed with follow-up processing. The assigned consultant will contact you within one hour. |
| Rejected         | 4            | Order not approved. If necessary, please review and resubmit.                                                          |

On the page, we can capture the order status value and dynamically display different messages accordingly. Below is a detailed explanation of how to implement this using if, else, and else if syntax.

##### 2.1.1 If Syntax

Using an if condition, you can display content when the condition is true. For example:

```
{{#if condition}}
  

Displayed Result

{{/if}} ``` In this case, "condition" should use Handlebars syntax (such as eq, gt, lt, etc.). Try this simple example: ``` {{#if (eq 1 1)}}

Displayed Result: 1 = 1

{{/if}} ``` The result is shown in the figures below: ![if Example 1](https://static-docs.nocobase.com/20250305115416.png) ![if Example 2](https://static-docs.nocobase.com/20250305115434.png) ##### 2.1.2 Else Syntax When the condition isn’t met, you can specify alternative content using else. For example: ``` {{#if (eq 1 2)}}

Displayed Result: 1 = 2

{{else}}

Displayed Result: 1 ≠ 2

{{/if}} ``` The outcome is as follows: ![else Example](https://static-docs.nocobase.com/20250305115524.png) ##### 2.1.3 Multiple Condition Checks To check multiple conditions, you can use else if. Example code: ``` {{#if (eq 1 7)}}

Displayed Result: 1 = 7

{{else if (eq 1 5)}}

Displayed Result: 1 = 5

{{else if (eq 1 4)}}

Displayed Result: 1 = 4

{{else}}

Displayed Result: 1 ≠ 7 ≠ 5 ≠ 3

{{/if}} ``` The corresponding effect is illustrated below: ![Multiple Conditions Example](https://static-docs.nocobase.com/20250305115719.png) #### 2.2 Displaying Effects After configuring the order statuses, the page will dynamically update to show content based on the specific status. See the image below: ![Dynamic Order Status Effect](https://static-docs.nocobase.com/202503040942-handlebar1.gif) The code for the page is as follows: ``` {{#if order.status}}
{{#if (eq order.status "1")}} ⏳ Pending Approval

Order submitted. Awaiting internal review.

{{else if (eq order.status "2")}} 💳 Pending Payment

Awaiting customer payment. Please monitor the status.

{{else if (eq order.status "3")}} ✔ Paid

Payment confirmed. Please proceed with subsequent processing. The assigned consultant will follow up with the customer within 1 hour.

{{else if (eq order.status "4")}} ✖ Rejected

Order approval was not passed. If necessary, please review and resubmit.

{{/if}}
{{else}}

No pending orders at this time.

{{/if}} ``` Try switching the order status to see if the page updates accordingly, and verify that your code works properly. #### 2.3 Displaying Order Details Besides displaying the order status, order details (such as the list of product details) are common requirements. Here, we can use the each syntax to implement this feature. ##### 2.3.1 Introduction to the Each Syntax The each helper is used to loop through a list. For example, for the array [1,2,3], you can write: ``` {{#each list}}

Displayed Result: {{this}}

Index: {{@index}}

{{/each}} ``` Within the loop, {{this}} represents the current element, and {{@index}} represents the current index. ##### 2.3.2 Product Details Example If you need to display all product information from an order, you can use the following code: ``` {{#each $nRecord.order_items}}

{{@index}}

{{this.id}}

{{this.price}}

{{this.quantity}}

{{this.product.name}}

--- {{/each}} ``` If you notice that no data appears on the page, make sure the order items field is properly set up to be displayed; otherwise, the system may consider this part of the data redundant and omit querying it. You might also notice that the product name (this.product.name) isn’t printed. This is because, similar to the previous situation, we also need to display the product object. After it is displayed, we can hide the related field in the linkage rule settings. ![Handlebars each Example 1](https://static-docs.nocobase.com/20250305122543_handlebar_each.gif) ![Handlebars each Example 2](https://static-docs.nocobase.com/20250305122543_each2.gif) ![Handlebars each Hidden](https://static-docs.nocobase.com/20250305122543_hidden_each.gif) #### 2.4 Final Product: Order Product List After completing the above steps, you will have implemented a complete template for displaying an order product list. Refer to the following code: ```html

Shopping Cart Items List

{{#if $nRecord.order_items}}
Total: {{$nRecord.order_items.length}} items, Total price: ¥{{$nRecord.total}}
{{#each $nRecord.order_items}} {{/each}}
Index Product Name Unit Price Quantity Subtotal
{{@index}} {{this.product.name}} ¥{{this.price}} {{this.quantity}} ¥{{multiply this.price this.quantity}} {{#if this.out_of_stock}} Out of stock {{else if this.low_stock}} Low stock {{/if}}
{{else}}

The shopping cart is empty

{{/if}} ``` After running the template, you will see an effect similar to the image below: ![Order Product List Effect](https://static-docs.nocobase.com/20250305124125.png) To better demonstrate the flexibility of Handlebars, we have added the “Out of Stock” and “Low Stock” indicators in the order details: - When out_of_stock is true, “Out of Stock” is displayed and the product row appears in red. - When low_stock is true, a “Low Stock” note is shown in orange. ![Additional Effect: Out of Stock and Low Stock](https://static-docs.nocobase.com/20250305130258.png) ## 3 Summary and Recommendations Through the explanation above, you have learned how to use Handlebars to achieve dynamic rendering in Markdown templates, including core syntaxes like if/else conditions and each loops. In practical development, for more complex logic, it is recommended to combine linkage rules, calculated fields, workflows, or script nodes to enhance flexibility and scalability. Hope that by practicing these techniques, you will master them and apply them effectively in your projects. Keep exploring and discovering new possibilities! --- If you encounter any issues during the operation, feel free to visit the [NocoBase community](https://forum.nocobase.com) or refer to the [official documentation](https://docs.nocobase.com). We hope this guide helps you successfully implement user registration auditing based on your actual needs and provides flexibility for further extensions. Wishing you smooth usage and project success! --- url: /tutorials/v1/22-deploy-faster.md --- # How to Deploy NocoBase Faster Many users may find that NocoBase runs slower than expected after deployment. This is often due to network environment, server configurations, or deployment architecture. Before diving into optimization techniques, let's first look at reference values for normal NocoBase loading speeds to avoid unnecessary concerns. ### NocoBase Normal Loading Speed Reference The following are loading speeds tested in the NocoBase demo environment: * Time required to enter the application for the first time by entering the URL: approximately 2 seconds * Time required to switch pages within the application: approximately 50-300 milliseconds Next, I'll share a series of simple yet effective deployment optimization techniques that can significantly improve access speed by adjusting deployment settings, without needing to modify any code: ## I. Network and Infrastructure Optimization ### 1. HTTP Protocol Version: Easily Embrace HTTP/2 【Prerequisites】 - **HTTPS Support Required**: This is important! Almost all modern browsers only support HTTP/2 over HTTPS connections, so you must configure SSL certificates first. - **Server Requirements**: You need to use server software that supports HTTP/2, such as Nginx 1.9.5+ or Apache 2.4.17+. - **TLS Version**: TLS 1.2 or higher is recommended (TLS 1.3 is best), as older SSL versions don't support HTTP/2. 【Tips】 Traditional HTTP/1.1 protocol has limitations when handling multiple requests—typically only 6-8 connections simultaneously, which is like waiting in line to buy tickets, easily causing delays. ![250416_http1_en](https://static-docs.nocobase.com/250416_http1_en.png) HTTP/2 uses "multiplexing" technology to handle multiple requests simultaneously, greatly accelerating resource loading; while the latest HTTP/3 performs even better in unstable networks, with excellent results. ![250416_http2_en](https://static-docs.nocobase.com/250416_http2_en.png) 【Optimization Suggestions】 - Make sure your web server has HTTP/2 support enabled, which is easy to configure on most servers (like Nginx, Caddy). - In Nginx, simply add the `http2` parameter after the listen directive: ```nginx listen 443 ssl http2; ``` 【Verification】 In your browser's developer panel, open the "Network" option, right-click and check "Protocol" to see the current connection protocol version: ![20250407145442](https://static-docs.nocobase.com/20250407145442.png) Based on our tests, overall speed improves by about 10%, with more significant performance improvements when the system has many blocks and resources. ### 2. Network Bandwidth: Bigger is Better, Flexible Billing 【Tips】 Just like highways are smoother than local roads, bandwidth determines data transmission efficiency. When NocoBase loads for the first time, it needs to download many frontend resources, and insufficient bandwidth can easily become a bottleneck. 【Optimization Suggestions】 - Choose sufficient bandwidth (50Mbps+ recommended for many users), don't skimp on this critical resource. - Recommend "pay-as-you-go" billing: many cloud providers offer this flexible model, allowing you to enjoy higher bandwidth during peak times while controlling costs during normal usage. ### 3. Network Latency and Server Geographic Location: Closer Means Faster 【Tips】 Latency is the waiting time for data transmission. Even with sufficient bandwidth, if the server is too far from users (e.g., users in China but server in the US), each request response may be slowed down due to the long distance. 【Optimization Suggestions】 - Try to deploy NocoBase in regions closer to your main user base. - If your users are globally distributed, consider using global acceleration services (such as Alibaba Cloud Global Accelerator or AWS Global Accelerator) to optimize network routing and reduce latency. 【Verification】 Use the ping command to test latency from different regions. This approach shows the most obvious improvement, with access speed increasing 1-3 times or more depending on the region. Across 12 time zones, 13 seconds: ![20250416130618](https://static-docs.nocobase.com/20250416130618.png) Across 2 time zones, 8 seconds: ![20250409131039](https://static-docs.nocobase.com/20250409131039.png) Current region, about 3 seconds: ![20250409130928](https://static-docs.nocobase.com/20250409130928.png) ## II. Deployment Architecture Optimization ### 4. Server Deployment and Proxy Methods: Choose the Most Suitable Architecture 【Prerequisites】 - **Server Permissions**: You need root or sudo access to configure services like Nginx. - **Basic Skills**: Some basic server configuration knowledge is required, but don't worry, specific configuration examples will be provided. - **Port Access**: Ensure your firewall allows access to ports 80 (HTTP) and 443 (HTTPS). 【Tips】 When users access NocoBase, requests go directly to your server. An appropriate deployment method allows your server to handle requests more efficiently, providing faster responses. 【Different Solutions and Recommendations】 **Starting NocoBase without using a reverse proxy for static resources (not recommended):** - Disadvantages: This method is simple but performs poorly when handling high concurrency or static files; suitable for development and testing only. - Recommendation: Please avoid this method if possible. > Reference "[Installation Documentation](https://v2.docs.nocobase.com/get-started/quickstart)" Without a reverse proxy, homepage loading takes about 6.1 seconds ![20250409131357](https://static-docs.nocobase.com/20250409131357.png) **Using Nginx / Caddy as reverse proxy (strongly recommended):** - Advantages: Reverse proxy servers can efficiently handle concurrent connections, serve static files, implement load balancing, and make HTTP/2 configuration simple. - Recommendation: In production environments, after application deployment (source code deployment / create-nocobase-app / Docker image), use Nginx or Caddy as a reverse proxy. > Reference "[Deployment Documentation](https://v2.docs.nocobase.com/get-started/deployment/production)" With Nginx proxy, homepage loading takes about 3-4 seconds ![20250409131541](https://static-docs.nocobase.com/20250409131541.png) ![20250416081410](https://static-docs.nocobase.com/20250416081410.png) **Using cluster deployment with load balancing (suitable for high concurrency and high availability scenarios):** - Advantages: By deploying multiple instances to handle requests, you can significantly improve overall system stability and concurrency capacity. - For specific deployment methods, see **[Cluster Mode](https://docs.nocobase.com/welcome/getting-started/deployment/cluster-mode)** ### 5. Using CDN to Accelerate Static Resources 【Prerequisites】 - **Domain Requirements**: You need a registered domain name and the ability to manage its DNS settings. - **SSL Certificate**: Most CDN services require SSL certificate configuration (you can use free Let's Encrypt certificates). - **Service Selection**: Choose appropriate CDN providers based on user regions (users in mainland China need CDNs with ICP filing). 【Tips】 CDN (Content Delivery Network) caches your static resources at nodes around the world, allowing users to get resources from the nearest node, like getting water from a nearby source, greatly reducing loading delays. Additionally, the greatest advantage of CDNs is their ability to **significantly reduce the load and bandwidth pressure on your application server**. When many users access NocoBase simultaneously without a CDN, all static resource requests (JavaScript, CSS, images, etc.) hit your server directly, potentially causing bandwidth saturation, performance degradation, or even server crashes. By offloading these requests to a CDN, your server can focus on processing core business logic, providing users with a more stable experience. ![20250416_0826](https://static-docs.nocobase.com/20250416_0826.png) 【Optimization Suggestions】• Configure your server to distribute static resource requests through CDN.• Choose suitable CDN providers based on user location: - Global users: Cloudflare, Akamai, AWS CloudFront; - Mainland China users: Alibaba Cloud CDN, Tencent Cloud CDN, Baidu Cloud Acceleration.Example configuration: ```nginx # Redirect static resources to CDN domain location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { rewrite ^(.*)$ https://your-cdn-domain.com$1 permanent; } ``` - For small projects, Cloudflare's free plan can provide good CDN acceleration: 1. Register a Cloudflare account and add your domain; 2. Modify DNS to point your domain to Cloudflare's servers; 3. Set appropriate cache levels in the control panel. **Special Note**: Even if all your users are in the same region, it's still strongly recommended to use a CDN as it effectively reduces server burden, improves overall system stability, especially during high traffic periods. ## III. Static Resource Optimization ### 6. Server Compression and Cache Configuration 【Prerequisites】 - **CPU Resources**: Compression increases server CPU load, so your server should have sufficient processing power. - **Nginx Module Support**: Gzip compression is generally built-in, but Brotli compression may require additional module installation. - **Configuration Permissions**: You need permission to modify server configurations. 【Tips】 By enabling compression and implementing reasonable cache strategies, you can significantly reduce data transfer volume and repeated requests, essentially "slimming down" your resources to make loading speeds take off. ![20250416081719](https://static-docs.nocobase.com/20250416081719.png) 【Optimization Suggestions】 - Simplest solution: Use Cloudflare's free CDN service, which automatically enables Gzip compression. - Enable Gzip or Brotli compression, which can be set in Nginx like this: ```nginx # Enable Gzip compression gzip on; gzip_comp_level 6; gzip_min_length 1000; gzip_proxied any; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # If Brotli compression is supported, enable it for more efficient compression brotli on; brotli_comp_level 6; brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; ``` - Set appropriate cache headers for static resources to reduce repeated loading: ```nginx location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 30d; add_header Cache-Control "public, max-age=2592000"; access_log off; } ``` ### 7. Using SSL/TLS and Optimizing Performance 【Prerequisites】 - **SSL Certificate**: You need a valid SSL certificate (you can use free Let's Encrypt certificates). - **Server Configuration Permissions**: You need to be able to modify SSL configurations. - **DNS Configuration**: Configure reliable DNS resolvers for OCSP Stapling. 【Tips】 Security always comes first, but improper HTTPS configuration can add some delay. Here are some optimization tricks to help you maintain high performance while ensuring security. 【Optimization Suggestions】 - Use TLS 1.3, which is currently the fastest TLS version. Configure in Nginx: ```nginx ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ``` - Enable OCSP Stapling to reduce certificate verification time: ```nginx ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; ``` - Reduce repeated handshake time through session reuse: ```nginx ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ``` 【Cross-Region Optimization Effect】 **Special Note**: The following shows optimization effects in a cross-region/cross-12-timezone scenario, which is fundamentally different from the local access scenario (about 3 seconds) mentioned earlier. Network latency caused by geographical distance is unavoidable, but optimization can still significantly improve speed: With Http2 + CDN caching + Gzip compression + Brotli compression combined: Before optimization (cross-region access), 13 seconds: ![20250416130618](https://static-docs.nocobase.com/20250416130618.png) After optimization (cross-region access), 8 seconds: ![20250409173528](https://static-docs.nocobase.com/20250409173528.png) This example shows that even when geographical locations are far apart, appropriate optimization measures can still reduce loading time by about 40%, greatly improving user experience. ## IV. Monitoring and Troubleshooting ### 8. Performance Monitoring and Basic Analysis 【Prerequisites】 - **Accessibility**: Your website must be publicly accessible to use most online testing tools. - **Basic Skills**: You need to understand the basic meaning of performance metrics, though we'll explain each key indicator. 【Tips】 It's difficult to optimize precisely without knowing where the bottlenecks are. We recommend using some free tools to monitor website performance to help identify problems. 【Optimization Suggestions】 **Use the following free tools to check website performance:** - [Google PageSpeed Insights](https://pagespeed.web.dev/) - [WebPageTest](https://www.webpagetest.org/) - [Pingdom](https://tools.pingdom.com/) **Focus on these key metrics:** - Page load time - Server response time - DNS resolution time - SSL handshake time **Common problem solutions:** - Slow DNS resolution? Consider changing DNS service or enabling DNS pre-resolution. - Slow SSL handshake? Optimize SSL configuration, enable session reuse. - Slow server response? Check server resources, upgrade if necessary. - Slow static resource loading? Try implementing CDN and adjusting cache strategies. ## Deployment Optimization Quick Checklist The following checklist can help you quickly check and optimize your NocoBase deployment: 1. **HTTP Version Check** - [ ] HTTPS enabled (prerequisite for HTTP/2) - [ ] HTTP/2 enabled - [ ] Consider supporting HTTP/3 if conditions allow 2. **Bandwidth Assessment** - [ ] Sufficient server bandwidth (at least 10Mbps recommended, 50Mbps+ preferred) - [ ] Consider pay-as-you-go billing model rather than fixed bandwidth 3. **Server Location Selection** - [ ] Server location should be close to user regions - [ ] Consider using global acceleration services for worldwide users 4. **Deployment Architecture** - [ ] Use Nginx/Caddy as reverse proxy to separate static resources from API - [ ] If needed, adopt multi-instance deployment and load balancing technology 5. **CDN Implementation** - [ ] Accelerate static resource distribution through CDN - [ ] Configure appropriate caching strategies - [ ] Ensure CDN supports HTTP/2 or HTTP/3 6. **Compression and Caching** - [ ] Enable Gzip or Brotli compression - [ ] Set appropriate browser cache headers 7. **SSL/TLS Optimization** - [ ] Use TLS 1.3 to improve handshake speed - [ ] Enable OCSP Stapling - [ ] Configure SSL session reuse 8. **Performance Monitoring** - [ ] Regularly use performance testing tools to evaluate your website - [ ] Monitor key metrics (loading, response, resolution, handshake times) - [ ] Optimize based on identified issues ## Frequently Asked Questions 【Q】My server is deployed in a different region than my users, causing slow access. What should I do? 【A】The best solution is to choose a cloud server in the same region as your primary users. If that's not possible, you can also: 1. Use a CDN service to accelerate static resources; 2. Utilize global acceleration services to optimize network routing; 3. Enable all compression and cache optimization measures as much as possible. 【Q】Why is my NocoBase slow on first load but fast afterward? 【A】Slow first load is normal because it needs to download many resources initially. Using our official Demo as an example, the first load typically takes about 3 seconds. Subsequent daily access when entering the URL takes about 1-2 seconds, while navigating between pages within the application is very fast at approximately 50-300 milliseconds, with very low latency. ![20250416130719](https://static-docs.nocobase.com/20250416130719.png) For excessively long loading times, there's still room for optimization: 1. Ensure HTTP/2 is enabled; 2. Implement CDN acceleration; 3. Enable Gzip/Brotli compression; 4. Check if server bandwidth is sufficient. 【Q】I'm currently using shared hosting and cannot modify Nginx configuration. What should I do? 【A】In this case, although optimization options are fewer, we still recommend: 1. Try using CDN services (like Cloudflare); 2. Optimize parameters that can be adjusted within the application; 3. If conditions allow, consider upgrading to a VPS that supports more custom configurations. --- Through these simple yet practical deployment optimization strategies, you can significantly improve NocoBase access speed and provide users with a smoother experience. Many optimization measures can be completed within a few hours, require no code changes, and easily show results. Daily operations: ![2504161639operatio_user2](https://static-docs.nocobase.com/2504161639operatio_user2.gif) ![2504161639operation3](https://static-docs.nocobase.com/2504161639operation3.gif) --- url: /tutorials/v1/23-crm-pipeline.md --- # CRM Sales Pipeline Visualization ## 1. Introduction ### 1.1 Preface This chapter is the second part of the [How to Implement CRM Lead Conversion in NocoBase](https://www.nocobase.com/en/tutorials/how-to-implement-lead-conversion-in-nocobase) tutorial series. In the previous chapter, we covered the fundamentals of lead conversion, including creating the necessary collections, configuring data management pages, and implementing the conversion of leads to companies, contacts, and opportunities. This chapter will focus on implementing the lead follow-up process and status management. 🎉 [NocoBase CRM Solution is Now Live — Ready for You to Explore](https://www.nocobase.com/en/blog/crm-solution) ### 1.2 Chapter Objective In this chapter, we will learn how to implement CRM lead conversion in NocoBase. Through lead follow-up and status management, you can boost operational efficiency and achieve more refined sales process control. ### 1.3 Preview of the Final Outcome In the previous chapter, we explained how to associate data between leads and the Company, Contact, and Opportunity collections. Now, we focus on the Lead module, primarily discussing how to perform lead follow-up and status management. Please watch the following demo: ![](https://static-docs.nocobase.com/202502250226-transfer3.gif) ## 2. Structure of the Lead Collection ### 2.1 Introduction to the Lead Collection In the lead follow-up functionality, the "status" field plays a crucial role. It not only reflects the current progress of the lead (e.g., Unqualified, New, Working, Nurturing, In Transaction, Completed) but also drives the dynamic display and changes of the form. The following table block shows the field structure of the Lead collection along with its detailed description: | Field name | Display Name | Field Interface | Description | | -------------- | ------------------ | ---------------- | ------------------------------------------------------------------------------------- | | id | **Id** | Integer | Primary key | | account_id | **account_id** | Integer | Foreign key of the ACCOUNT collection | | contact_id | **contact_id** | Integer | Foreign key of the CONTACT collection | | opportunity_id | **opportunity_id** | Integer | Foreign key of the OPPORTUNITY collection | | name | **Lead Name** | Single line text | Name of the prospective customer | | company | **Company Name** | Single line text | Name of the prospective customer's company | | email | **Email** | Email | Email address of the prospective customer | | phone | **Contact Number** | Phone | Contact number | | status | **Status** | Single select | Current lead status (Unqualified, New, Working, Nurturing, In Transaction, Completed) | | Account | **Company** | Many to one | Linked to the Company collection | | Contact | **Contact** | Many to one | Linked to the Contact collection | | Opportunity | **Opportunity** | Many to one | Linked to the Opportunity collection | ## 3. Creating the Leads Table Block and Detail Block ### 3.1 Instructions for Creation First, we need to create a "Leads" table block to display the necessary fields. At the same time, configure a detail block on the right side of the page so that when you click on a record, the corresponding details are automatically displayed. Please refer to the demo below: ![](https://static-docs.nocobase.com/20250226090009.png) ## 4. Configuring Action Buttons ### 4.1 Overall Description of the Buttons To meet various operational needs, we need to create a total of 10 buttons. Each button will display differently (hidden, active, or disabled) based on the record's status, guiding the user through the correct business process. ![20250311083825](https://static-docs.nocobase.com/20250311083825.png) ### 4.2 Detailed Configuration for Each Function Button | Button | Style | Action | Linkage Rule | | --------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | | Edit Button | Edit operation | — | Automatically disabled when the record's status is "Completed" to prevent unnecessary editing. | | Unqualified Button (Active) | "Unqualified >" | Updates the record's status to "Unqualified". | Displayed by default; disabled when status is "Completed". | | New Button (Inactive) | Update data operation, "New >" | Sets status to "New", shows "New" success notification. | Hidden if record's status is not "Unqualified". (Should be active if record is already at "New" or later status) | | New Button (Active) | Update data operation, "New >" | Updates the record's status to "New". | Hidden when status is "Unqualified"; disabled when status is "Completed". | | Working Button (Inactive) | Update data operation, "Working >" | Updates status to "Working", shows "Working" success notification. | Hidden when record's status is not "Unqualified" or "New". | | Working Button (Active) | Update data operation, "Working >" | Updates the record's status to "Working". | Hidden when status is "Unqualified" or "New"; disabled when status is "Completed". | | Nurturing Button (Inactive) | Update data operation, "Nurturing >" | Sets status to "Nurturing", shows "Nurturing" success notification. | Hidden when record's status is not "Unqualified", "New", or "Working". | | Nurturing Button (Active) | Update data operation, "Nurturing >" | Updates the record's status to "Nurturing". | Hidden when status is "Unqualified", "New", or "Working"; disabled when status is "Completed". | | Transfer Button | Edit operation, "transfer", icon: "√" | Opens conversion form, upon submission updates status to "Completed". | Hidden when the record's status is "Completed" to prevent duplicate transfers. | | Transferred Button (Active) | View operation, "transferred", icon: "√" | Only displays information after transfer completion, no editing functionality. | Only displayed when the record's status is "Completed"; hidden for other statuses. | - Linkage Rule Example: Working Button (Inactive) ![20250311084104](https://static-docs.nocobase.com/20250311084104.png) Working Button (Active) ![20250311083953](https://static-docs.nocobase.com/20250311083953.png) - Transfer Form: Transfer Button (Inactive) ![](https://static-docs.nocobase.com/20250226094223.png) Transfer Button (Active) ![](https://static-docs.nocobase.com/20250226094203.png) - Prompt shown during transfer submission: ![20250311084638](https://static-docs.nocobase.com/20250311084638.png) ### 4.3 Summary of Button Configurations - Each function provides different button styles for inactive and active states. - Linkage rules dynamically control the display (hidden or disabled) of the buttons based on the record's status, guiding sales personnel to follow the correct workflow. ## 5. Form Linkage Rule Settings ### 5.1 Rule 1: Display Only the Name - When the record is not confirmed or the status is empty, only the name is displayed. ![](https://static-docs.nocobase.com/20250226092629.png) ![](https://static-docs.nocobase.com/20250226092555.png) ### 5.2 Rule 2: Optimized Display Under "New" Status - When the status is "New", the company name is hidden and the contact information is displayed. ![](https://static-docs.nocobase.com/20250226092726.png) ## 6. Page Markdown Rules and Handlebars Syntax ### 6.1 Dynamic Text Display In the page, we use Handlebars syntax to dynamically display different prompt messages based on the record's status. Below are example codes for each status: When the status is "Unqualified": ```markdown {{#if (eq $nRecord.status "Unqualified")}} **Track the details of your unqualified leads.** If your lead is not interested in the product or has left the related company, it might be unqualified. - Record lessons learned for future reference - Save outreach details and contact information {{/if}} ``` When the status is "New": ```markdown {{#if (eq $nRecord.status "New")}} **Collect more information about this lead.** - Understand the potential customer's needs and interests - Gather basic contact information and company background - Determine follow-up priorities and methods {{/if}} ``` When the status is "Working": ```markdown {{#if (eq $nRecord.status "Working")}} **Proactively contact the lead and initially assess needs.** - Establish contact with the potential customer via phone/email - Understand the customer's problems and challenges - Preliminarily evaluate the match between customer needs and your products/services {{/if}} ``` When the status is "Nurturing": ```markdown {{#if (eq $nRecord.status "Nurturing")}} **Explore customer needs deeply and nurture the lead.** - Provide relevant product information or solution recommendations - Answer customer questions and address concerns - Evaluate the possibility of lead conversion {{/if}} ``` When the status is "Completed": ```markdown {{#if (eq $nRecord.status "Completed")}} **Lead has been successfully converted to a customer.** - Confirm that related company and contact records have been created - Create opportunity record and set up follow-up plans - Transfer relevant materials and communication records to the responsible sales personnel {{/if}} ``` ## 7. Displaying Associated Objects and Jump Links After Conversion ### 7.1 Description of Associated Objects After conversion, we want to display the associated objects (Company, Contact, Opportunity) along with links to their detail pages. You can find a detail popup, such as a company, and copy the link. ![20250311085502](https://static-docs.nocobase.com/20250311085502.png) Note: In other pop-ups or pages, the last part of the detail link (after filterbytk) represents the current object's id. For example: ```text {Base URL}/admin/w3yyu23uro0/popups/ki0wcnfruj6/filterbytk/{id} ``` ### 7.2 Generating Associated Links Using Handlebars For Company: ```markdown {{#if (eq $nRecord.status "Completed")}} **Company:** [{{$nRecord.account.name}}](w3yyu23uro0/popups/ki0wcnfruj6/filterbytk/{{$nRecord.account_id}}) {{/if}} ``` For Contact: ```markdown {{#if (eq $nRecord.status "Completed")}} **Contact:** [{{$nRecord.contact.name}}](1oqybfwrocb/popups/8bbsqy5bbpl/filterbytk/{{$nRecord.contact_id}}) {{/if}} ``` For Opportunity: ```markdown {{#if (eq $nRecord.status "Completed")}} **Opportunity:** [{{$nRecord.opportunity.name}}](si0io9rt6q6/popups/yyx8uflsowr/filterbytk/{{$nRecord.opportunity_id}}) {{/if}} ``` ![](https://static-docs.nocobase.com/20250226093501.png) ## 8. Hiding Associated Objects but Retaining Their Values To ensure that associated information is displayed properly after conversion, the statuses for "Company", "Contact", and "Opportunity" should be set to "hidden (retain value)". This way, even though these fields are not shown on the form, their values are still recorded and passed along. ![](https://static-docs.nocobase.com/20250226093906.png) ## 9. Preventing Status Modification After Conversion To prevent accidental changes to the status after conversion, we add a condition to all buttons: when the status is "Completed", all buttons will be disabled. ![](https://static-docs.nocobase.com/20250226094302.png) ## 10. Conclusion After completing all of the above steps, your lead follow-up conversion functionality is complete! Through this step-by-step guide, we hope you have gained a clearer understanding of how status-based form dynamics and linkages are implemented in NocoBase. Wishing you smooth operations and an enjoyable experience! --- url: /tutorials/v1/24-crm-demo-deploy.md --- # CRM Demo Deployment Guide To help you quickly and smoothly deploy this Demo to your own NocoBase environment, we provide two restoration methods. Please choose the most suitable one based on your user version and technical background. Before starting, please ensure: - You already have a basic NocoBase running environment. For main system installation, please refer to the more detailed [official installation documentation](https://docs.nocobase.com/welcome/getting-started/installation). - You have downloaded our CRM Demo files: - **Backup File** (approx. 21.2MB): [crm_demo_20250711.nbdata](https://static-docs.nocobase.com/crm_demo_20250711.nbdata) - For Method 1 - **SQL File** (approx. 9MB compressed): [crm_demo_20250711_sql.zip](https://static-docs.nocobase.com/crm_demo_20250711_sql.zip) - For Method 2 **Important Note**: This Demo is built on **PostgreSQL** database. Please ensure your environment uses PostgreSQL database. --- ### Method 1: Restore Using Backup Manager (Recommended for Pro/Enterprise Users) This method performs one-click restoration through NocoBase's built-in "[Backup Manager](https://docs.nocobase.com/handbook/backups)" (Pro/Enterprise Edition) plugin, with the simplest operation. However, it has certain requirements for environment and user version. #### Key Features * **Advantages**: 1. **Convenient Operation**: Can be completed in the UI interface, capable of completely restoring all configurations including plugins. 2. **Complete Restoration**: **Can restore all system files**, including template print files, uploaded files in table file fields, etc., ensuring Demo functionality integrity. * **Limitations**: 1. **Pro/Enterprise Edition Only**: "Backup Manager" is an enterprise-level plugin, only available to Pro/Enterprise users. 2. **Strict Environment Requirements**: Requires your database environment (version, case sensitivity settings, etc.) to be highly compatible with our backup creation environment. 3. **Plugin Dependencies**: If the Demo contains commercial plugins that you don't have in your local environment, restoration will fail. #### Operation Steps **Step 1: [Strongly Recommended] Start Application Using `full` Image** To avoid restoration failures due to missing database clients, we strongly recommend using the `full` version of the Docker image. It has all necessary supporting programs built-in, requiring no additional configuration. (Note: The image is built on 1.9.0-alpha.1, please pay attention to version compatibility) Example command to pull the image: ```bash docker pull nocobase/nocobase:1.9.0-alpha.3-full ``` Then use this image to start your NocoBase service. > **Note**: If you don't use the `full` image, you may need to manually install the `pg_dump` database client inside the container, which is cumbersome and unstable. **Step 2: Enable the "Backup Manager" Plugin** 1. Log into your NocoBase system. 2. Go to **`Plugin Management`** . 3. Find and enable the **`Backup Manager`** plugin. ![20250711014113](https://static-docs.nocobase.com/20250711014113.png) **Step 3: Restore from Local Backup File** 1. After enabling the plugin, refresh the page. 2. Go to **`System Management`** -> **`Backup Manager`** in the left menu. 3. Click the **`Restore from Local Backup`** button in the top right corner. ![20250711014216](https://static-docs.nocobase.com/20250711014216.png) 4. Drag the Demo backup file we provided (usually in `.zip` format) to the upload area. 5. Click **`Submit`** and wait patiently for the system to complete restoration, which may take anywhere from tens of seconds to several minutes. ![20250711014250](https://static-docs.nocobase.com/20250711014250.png) #### ⚠️ Important Notes * **Database Compatibility**: This is the most critical point of this method. Your PostgreSQL database **version, character set, case sensitivity settings** must match the Demo backup source file. Particularly, `schema` names must be consistent. * **Commercial Plugin Matching**: Please ensure you have and have enabled all commercial plugins required by the Demo, otherwise restoration will be interrupted. --- ### Method 2: Direct SQL File Import (Universal, More Suitable for Community Edition) This method restores data by directly operating the database, bypassing the "Backup Manager" plugin, thus having no Pro/Enterprise plugin limitations. #### Key Features * **Advantages**: 1. **No Version Restrictions**: Suitable for all NocoBase users, including community edition. 2. **High Compatibility**: Does not depend on in-app `dump` tools, only requires database connection capability. 3. **High Fault Tolerance**: If the Demo contains commercial plugins you don't have (such as ECharts charts), related functions will not be enabled, but won't affect normal use of other functions, and the application can start successfully. * **Limitations**: 1. **Database Operation Skills Required**: Requires users to have basic database operation capabilities, such as how to execute a `.sql` file. 2. **⚠️ System File Loss**: **This method will lose all system files**, including template print files, uploaded files in table file fields, etc. This means: - Print template functions may not work properly - Uploaded images, documents, and other files will be lost - Functions involving file fields will be affected #### Operation Steps **Step 1: Prepare a Clean Database** Prepare a brand new, empty database for the Demo data you're about to import. **Step 2: Import the `.sql` File into the Database** Get the Demo database file we provided (usually in `.sql` format) and import its contents into the database you prepared in the previous step. There are multiple execution methods, depending on your environment: * **Option A: Through Server Command Line (Docker Example)** If you installed NocoBase and database using Docker, you can upload the `.sql` file to the server, then use `docker exec` command to perform the import. Assuming your PostgreSQL container name is `my-nocobase-db` and the file name is `crm_demo.sql`: ```bash # Copy sql file into the container docker cp crm_demo.sql my-nocobase-db:/tmp/ # Enter container to execute import command docker exec -it my-nocobase-db psql -U your_username -d your_database_name -f /tmp/crm_demo.sql ``` * **Option B: Through Remote Database Client** If your database exposes a port, you can use any graphical database client (such as DBeaver, Navicat, pgAdmin, etc.) to connect to the database, create a new query window, paste the entire contents of the `.sql` file, and execute it. **Step 3: Connect Database and Start Application** Configure your NocoBase startup parameters (such as environment variables `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USER`, `DB_PASSWORD`, etc.) to point to the database you just imported data into. Then, start the NocoBase service normally. ![img_v3_02o3_eb637bd2-88c3-400b-8421-1ac2057d1aag](https://static-docs.nocobase.com/img_v3_02o3_eb637bd2-88c3-400b-8421-1ac2057d1aag.png) #### ⚠️ Important Notes * **Database Permissions**: This method requires you to have an account and password that can directly operate the database. * **Plugin Status**: After successful import, commercial plugin data in the system exists, but if you haven't installed and enabled corresponding plugins locally, related functions (such as Echarts charts, specific fields, etc.) will not display and work, but this won't cause application crashes. --- ### Summary and Comparison | Feature | Method 1: Backup Manager | Method 2: Direct SQL Import | | :------------------------ | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Applicable Users** | **Pro/Enterprise Edition** users | **All users** (including Community Edition) | | **Operation Simplicity** | ⭐⭐⭐⭐⭐ (Very easy, UI-based operation) | ⭐⭐⭐ (Requires basic database knowledge) | | **Environment Needs** | **Strict**: Database, system version, etc. must be highly compatible | **General**: Only requires database compatibility | | **Plugin Dependency** | **Strong dependency**: Restoration will fail if any required plugin is missing | **Depends on plugins**. Data can be imported independently, and the system has basic functionality. However, if the corresponding plugin is missing, related features will **not be usable at all**. | | **System Files** | **✅ Fully preserved** (print templates, uploaded files, etc.) | **❌ Will be lost** (print templates, uploaded files, etc.) | | **Recommended Scenarios** | Pro/Enterprise users with controllable, consistent environments, requiring complete functionality demonstration | Missing some plugins, pursuing high compatibility and flexibility, non-Pro/Enterprise users, acceptable file function loss | We hope this tutorial helps you successfully deploy the CRM Demo. If you encounter any problems during the operation, please feel free to contact us! --- url: /tutorials/v1/index.md --- # Task Management System Overview ## Introduction Welcome to the world of **NocoBase**! In today’s rapidly evolving business landscape, companies and development teams often encounter challenges like: - **Frequent changes in business needs**, making it difficult for traditional development approaches to respond quickly. - **Tight and urgent delivery deadlines**, with complex processes that lead to inefficiencies. - **Limited capabilities of no-code platforms**, which may not meet complex needs. - **Challenges in ensuring data privacy and system stability**. - **Difficulties integrating with existing systems**, impacting overall efficiency. - **User or application-based charges**, making cost management challenging. **NocoBase** was designed specifically to tackle these issues. As a **highly extensible no-code development platform**, NocoBase offers the following unique advantages: - **Free, open-source, flexible, and fast**: Open-source code with active community support. Install, deploy, and develop in minutes. - **Highly extensible**: Micro-kernel architecture with modular design, where all features are offered as plugins. - **Unique core concepts**: Build systems through the combination of data sources, blocks, and operations, ensuring a smooth, intuitive experience. - **What you see is what you get**: An intuitive UI editor for seamless interface design. - **Data-driven**: Supports multiple data sources, with data structure and interface decoupled. ## NocoBase’s Design Goals NocoBase strikes a fine balance between **ease of use**, **powerful functionality**, and **low complexity**. It offers a wide range of functional modules to meet diverse needs while keeping the user interface simple and intuitive. Additionally, its **plugin mechanism** allows users to expand platform capabilities, enabling highly customized extensions that ensure system flexibility and sustainability. --- With this introduction, you should now have a foundational understanding of **NocoBase**. This tutorial series will emphasize hands-on project practice, guiding you step-by-step through NocoBase’s core concepts and setup process, and ultimately enabling you to build a streamlined, efficient task management system. ## Why Choose a Task Management System? A task management system is an ideal introductory project for beginners: - It closely aligns with everyday needs. - It has a simple structure with strong extensibility, allowing it to expand from basic task management into a complete project management system. This tutorial will start with beginner-friendly features, covering NocoBase’s core modules and operations, such as task creation, commenting, permission management, and notification settings. By the end, you’ll have a comprehensive understanding of NocoBase’s foundational functions. ### Core Concepts Integrated with Task Management Throughout each chapter, we’ll explore NocoBase’s core concepts in depth, including: - **Collections**: The system’s foundational data structure, such as collections for tasks, users, and comments, providing the system’s information base. - **Blocks**: Data displays on pages, supporting multiple presentation styles. Blocks allow for dynamic data presentations in tasks like creation, editing, viewing, and management. Additional features (e.g., comment blocks) can also be added through plugins. - **Actions**: Actions for creating, reading, updating, and deleting data, allowing users to manage tasks and comments to meet various needs. - **Plugin Extensions**: All features in NocoBase are delivered through plugins, offering high extensibility. This tutorial will introduce Markdown and comment plugins to add useful features for task descriptions and team collaboration. - **Workflow**: One of NocoBase’s highlights. This tutorial will walk you through basic automated workflows, such as task owner reminders, to give you a firsthand experience of the power of workflows. - ...... Ready to get started? Let’s dive in, beginning with [the interface and installation](https://www.nocobase.com/en/tutorials/task-tutorial-beginners-guide), as we build your own task management system step by step! --- url: /ui-development-action/index.md --- # Action Extensions --- url: /ui-development-action/write-an-action-plugin.md --- --- url: /ui-development-block/index.md --- # Block Extensions Overview In NocoBase 2.0, the block extension mechanism has been significantly simplified. Developers only need to inherit the corresponding **FlowModel** base class and implement related interface methods (mainly the `renderComponent()` method) to quickly customize blocks. ## Block Categories NocoBase categorizes blocks into three types, displayed in groups in the configuration interface: - **Data blocks**: Blocks that inherit from `DataBlockModel` or `CollectionBlockModel` - **Filter blocks**: Blocks that inherit from `FilterBlockModel` - **Other blocks**: Blocks that directly inherit from `BlockModel` > The block grouping is determined by the corresponding base class. The classification logic is based on inheritance relationships and requires no additional configuration. ## Base Class Description The system provides four base classes for extensions: ### BlockModel **Basic Block Model**, the most versatile block base class. - Suitable for display-only blocks that don't depend on data - Categorized into **Other blocks** group - Applicable to personalized scenarios ### DataBlockModel **Data Block Model (not bound to data table)**, for blocks with custom data sources. - Not directly bound to a data table, can customize data fetching logic - Categorized into **Data blocks** group - Applicable to: calling external APIs, custom data processing, statistical charts, etc. ### CollectionBlockModel **Collection Block Model**, for blocks that need to be bound to a data table. - Requires binding to a data table model base class - Categorized into **Data blocks** group - Applicable to: lists, forms, kanban boards, and other blocks that clearly depend on a specific data table ### FilterBlockModel **Filter Block Model**, for building filter condition blocks. - Model base class for building filter conditions - Categorized into **Filter blocks** group - Usually works in conjunction with data blocks ## How to Choose a Base Class When selecting a base class, you can follow these principles: - **Need to bind to a data table**: Prioritize `CollectionBlockModel` - **Custom data source**: Choose `DataBlockModel` - **For setting filter conditions and working with data blocks**: Choose `FilterBlockModel` - **Not sure how to categorize**: Choose `BlockModel` ## Quick Start Creating a custom block only requires three steps: 1. Inherit the corresponding base class (e.g., `BlockModel`) 2. Implement the `renderComponent()` method to return a React component 3. Register the block model in the plugin For detailed examples, please refer to [Write a Block Plugin](./write-a-block-plugin). --- url: /ui-development-block/write-a-block-plugin.md --- # Write Your First Block Plugin Before starting, it's recommended to read "[Write Your First Plugin](../plugin-development/write-your-first-plugin.md)" to learn how to quickly create a basic plugin. Next, we'll extend it by adding a simple Block feature. ## Step 1: Create Block Model File Create a new file in the plugin directory: `client/models/SimpleBlockModel.tsx` ## Step 2: Write Model Content Define and implement a basic block model in the file, including its rendering logic: ```tsx import { BlockModel } from '@nocobase/client'; import React from 'react'; import { tExpr } from '../utils'; export class SimpleBlockModel extends BlockModel { renderComponent() { return (

Hello, NocoBase!

This is a simple block rendered by SimpleBlockModel.

); } } SimpleBlockModel.define({ label: tExpr('Hello block'), }); ``` ## Step 3: Register Block Model Export the newly created model in the `client/models/index.ts` file: ```ts import { ModelConstructor } from '@nocobase/flow-engine'; import { SimpleBlockModel } from './SimpleBlockModel'; export default { SimpleBlockModel, } as Record; ``` ## Step 4: Activate and Experience the Block After enabling the plugin, you'll see the new **Hello block** option in the "Add Block" dropdown menu. Demo effect: ![20251102223200_rec_](https://static-docs.nocobase.com/20251102223200_rec_.gif) ## Step 5: Add Configuration Capability to Block Next, we'll add configurable functionality to the block through **Flow**, enabling users to edit block content in the interface. Continue editing the `SimpleBlockModel.tsx` file: ```tsx import { BlockModel } from '@nocobase/client'; import React from 'react'; import { tExpr } from '../locale'; export class SimpleBlockModel extends BlockModel { renderComponent() { return
; } } SimpleBlockModel.define({ label: tExpr('Simple block'), }); SimpleBlockModel.registerFlow({ key: 'flow1', title: tExpr('Simple Block Flow'), on: 'beforeRender', steps: { editHtml: { title: tExpr('Edit HTML Content'), uiSchema: { html: { type: 'string', title: tExpr('HTML Content'), 'x-decorator': 'FormItem', 'x-component': 'Input.TextArea', }, }, defaultParams: { html: `

This is a simple block

You can edit the HTML content.

`, }, handler(ctx, params) { ctx.model.props.html = params.html; }, }, }, }); ``` Demo effect: ![20251102222856_rec_](https://static-docs.nocobase.com/20251102222856_rec_.gif) ## Summary This article introduces how to create a simple block plugin, including: - How to define and implement a block model - How to register a block model - How to add configurable functionality through Flow Complete source code reference: [Simple Block Example](https://github.com/nocobase/nocobase/tree/develop/packages/plugins/%40nocobase-example/plugin-simple-block) --- url: /ui-development-field/index.md --- # Field Extensions --- url: /ui-development-field/write-a-field-plugin.md --- --- url: /users-permissions/acl/dev/permission-tab.md --- # Extending permission configuration tabs Below is an example of the "Mobile Menu" configuration item, demonstrating how to extend a new permission configuration tab. The effect is shown in the figure below: ![20240903210248](https://static-docs.nocobase.com/20240903210248.png) The code is as follows: ```typescript import { Plugin } from '@nocobase/client'; import PluginACLClient from '@nocobase/plugin-acl/client'; class PluginMobileClient extends Plugin { async load() { const aclInstance = this.app.pm.get(PluginACLClient); aclInstance?.settingsUI.addPermissionsTab(({ t, TabLayout, activeKey }) => ({ key: 'mobile-menu', label: t('Mobile menu', { ns: 'plugin-mobile', }), children: ( ), })); } } ``` First, we need to obtain an instance of the `PluginACLClient` plugin ([other methods to obtain plugin instances](/plugin-development/client/plugin#get-plugin-instance)), and add a new permission configuration tab using the `settingsUI.addPermissionsTab` method. In this example, we added a permission configuration tab named "Mobile Menu". The value of the `settingsUI` property is an instance of a class named `ACLSettingsUI`, and its type information is as follows: ```typescript import { TabsProps } from 'antd/es/tabs/index'; interface ACLSettingsUI { addPermissionsTab(tab: Tab | TabCallback): void; getPermissionsTabs(props: PermissionsTabsProps): Tab[]; } type Tab = TabsProps['items'][0]; type TabCallback = (props: PermissionsTabsProps) => Tab; interface PermissionsTabsProps { /** * the key of the currently active tab panel */ activeKey: string; /** * the currently selected role */ role: Role; /** * translation function */ t: TFunction; /** * used to constrain the size of the container in the Tab */ TabLayout: React.FC; } ``` --- url: /users-permissions/acl/permissions.md --- # Configuring Permissions ## General Permission Settings ![](https://static-docs.nocobase.com/119a9650259f9be71210121d0d3a435d.png) ### Configuration Permissions 1. **Allows to configure interface**: This permission governs whether a user can configure the interface. Activating it adds a UI configuration button. The "admin" role has this permission enabled by default. 2. **Allows to install, activate, disable plugins**: This permission dictates whether a user can enable or disable plugins. When active, the user gains access to the plugin manager interface. The "admin" role has this permission enabled by default. 3. **Allows to configure plugins**: This permission lets the user configure plugin parameters or manage plugin backend data. The "admin" role has this permission enabled by default. 4. **Allows to clear cache, reboot application**: This permission is tied to system maintenance tasks like clearing the cache and restarting the application. Once activated, related operation buttons appear in the personal center. This permission is disabled by default. 5. **New menu items are allowed to be accessed by default**: Newly created menus are accessible by default, and this setting is enabled by default. ### Global Action Permissions Global action permissions apply universally to all collections and are categorized by operation type. These permissions can be configured based on data scope: all data or the user's own data. The former allows operations on the entire collection, while the latter restricts operations to data relevant to the user. ## Collection Action Permissions ![](https://static-docs.nocobase.com/6a6e0281391cecdea5b5218e6173c5d7.png) ![](https://static-docs.nocobase.com/9814140434ff9e1bf028a6c282a5a165.png) Collection action permissions allow fine-tuning of global action permissions by configuring access to resources within each collection. These permissions are divided into two aspects: 1. **Action permissions**: These include adding, viewing, editing, deleting, exporting, and importing actions. Permissions are set based on data scope: - **All records**: Grants the user the ability to perform actions on all records within the collection. - **Own records**: Restricts the user to perform actions only on records they have created. 2. **Field Permissions**: Field permissions enable you to set specific permissions for each field during different operations. For instance, certain fields can be configured to be view-only, without editing privileges. ## Menu Access Permissions Menu access permissions control access based on menu items. ![](https://static-docs.nocobase.com/28eddfc843d27641162d9129e3b6e33f.png) ## Plugin Configuration Permissions Plugin configuration permissions control the ability to configure specific plugin parameters. When enabled, the corresponding plugin management interface appears in the admin center. ![](https://static-docs.nocobase.com/5a742ae20a9de93dc2722468b9fd7475.png) --- url: /users-permissions/acl/role.md --- # Roles ## Management Center ### Role Management ![](https://static-docs.nocobase.com/da7083c67d794e23dc6eb0f85b1de86c.png) The application comes with two predefined roles: "Admin" and "Member," each with distinct default permission settings tailored to their functionalities. ### Adding, Deleting, and Modifying Roles The role identifier, a unique system identifier, allows customization of default roles, but the system's predefined roles cannot be deleted. ![](https://static-docs.nocobase.com/35f323b346db4f9f12f9bee4dea63302.png) ### Setting the Default Role The default role is the one automatically assigned to new users if no specific role is provided during their creation. ![](https://static-docs.nocobase.com/f41bba7ff55ca28715c486dc45bc1708.png) ## Personal Center ### Role Switching Users can be assigned multiple roles and switch between them in the personal center. ![](https://static-docs.nocobase.com/e331d11ec1ca3b8b7e0472105b167819.png) The default role when logging in is determined by the most recently switched role (this value updates with each switch) or, if not applicable, the first role (system default role). --- url: /users-permissions/acl/ui.md --- # Application in UI ## Data Block Permissions Visibility of data blocks in a collection is controlled by view action permissions, with individual configurations taking precedence over global settings. For example, under global permissions, the "admin" role has full access, but the Orders collection may have individual permissions configured, making it invisible. Global permission configuration: ![](https://static-docs.nocobase.com/3d026311739c7cf5fdcd03f710d09bc4.png) Orders collection individual permission configuration: ![](https://static-docs.nocobase.com/a88caba1cad47001c1610bf402a4a2c1.png) In the UI, all blocks in the Orders collection are not displayed. Complete configuration process: ![](https://static-docs.nocobase.com/b283c004ffe0b746fddbffcf4f27b1df.gif) ## Field Permissions **View**: Determines whether specific fields are visible at the field level, allowing control over which fields are visible to certain roles within the Orders collection. ![](https://static-docs.nocobase.com/30dea84d984d95039e6f7b180955a6cf.png) In the UI, only fields with configured permissions are visible within the Orders collection block. System fields (Id, CreatedAt, LastUpdatedAt) retain view permissions even without specific configuration. ![](https://static-docs.nocobase.com/40cc49b517efe701147fd2e799e79dcc.png) - **Edit**: Controls whether fields can be edited and saved (updated). Configure edit permissions for Orders collection fields (quantity and associated items have edit permissions): ![](https://static-docs.nocobase.com/6531ca4122f0887547b5719e2146ba93.png) In the UI, only fields with edit permissions are shown in the edit action form block within the Orders collection. ![](https://static-docs.nocobase.com/12982450c311ec1bf87eb9dc5fb04650.png) Complete configuration process: ![](https://static-docs.nocobase.com/1dbe559a9579c2e052e194e50edc74a7.gif) - **Add**: Determines whether fields can be added (created). Configure add permissions for Orders collection fields (order number, quantity, items, and shipment have add permissions): ![](https://static-docs.nocobase.com/3ab1bbe41e61915e920fd257f2e0da7e.png) In the UI, only fields with add permissions are displayed within the add action form block of the Orders collection. ![](https://static-docs.nocobase.com/8d0c07893b63771c428974f9e126bf35.png) - **Export**: Controls whether fields can be exported. - **Import**: Controls whether fields support import. ## Action Permissions Individually configured permissions take the highest priority. If specific permissions are configured, they override global settings; otherwise, the global settings are applied. - **Add**: Controls whether the add action button is visible within a block. Configure individual action permissions for the Orders collection to allow adding: ![](https://static-docs.nocobase.com/2e3123b5dbc72ae78942481360626629.png) When the add action is permitted, the add button appears within the action area of the Orders collection block in the UI. ![](https://static-docs.nocobase.com/f0458980d450544d94c73160d75ba96c.png) - **View**: Determines whether the data block is visible. Global permission configuration (no view permission): ![](https://static-docs.nocobase.com/6e4a1e6ea92f50bf84959dedbf1d5683.png) Orders collection individual permission configuration: ![](https://static-docs.nocobase.com/f2dd142a40fe19fb657071fd901b2291.png) In the UI, data blocks for all other collections remain hidden, but the Orders collection block is shown. Complete example configuration process: ![](https://static-docs.nocobase.com/b92f0edc51a27b52e85cdeb76271b936.gif) - **Edit**: Controls whether the edit action button is displayed within a block. ![](https://static-docs.nocobase.com/fb1c0290e2a833f1c2b415c761e54c45.gif) Action permissions can be further refined by setting the data scope. For example, setting the Orders collection so users can only edit their own data: ![](https://static-docs.nocobase.com/b082308f62a3a9084cab78a370c14a9f.gif) - **Delete**: Controls whether the delete action button is visible within a block. ![](https://static-docs.nocobase.com/021c9e79bcc1ad221b606a9555ff5644.gif) - **Export**: Controls whether the export action button is visible within a block. - **Import**: Controls whether the import action button is visible within a block. ## Association Permissions ### As a Field - The permissions of an association field are controlled by the field permissions of the source collection. This controls whether the entire association field component is displayed. For example, in the Orders collection, the association field "Customer" only has view, import, and export permissions. ![](https://static-docs.nocobase.com/d0dc797aae73feeabc436af285dd4f59.png) In the UI, this means the "Customer" association field will not be displayed in the add and edit action blocks of the Orders collection. Complete example configuration process: ![](https://static-docs.nocobase.com/372f8a4f414feea097c23b2ba326c0ef.gif) - The permissions for fields within the association field component (such as a sub-table or sub-form) are determined by the permissions of the target collection. When the association field component is a sub-form: As shown below, the "Customer" association field in the Orders collection has all permissions, while the Customers collection itself is set to read-only. Individual permission configuration for the Orders collection, where the "Customer" association field has all field permissions: ![](https://static-docs.nocobase.com/3a3ab9722f14a7b3a35361219d67fa40.png) Individual permission configuration for the Customers collection, where fields have view-only permissions: ![](https://static-docs.nocobase.com/46704d179b931006a9a22852e6c5089e.png) In the UI, the "Customer" association field is visible in the Orders collection block. However, when switched to a sub-form, the fields within the sub-form are visible in the details view but are not displayed in the add and edit actions. Complete example configuration process: ![](https://static-docs.nocobase.com/932dbf6ac46e36ee357ff3e8b9ea1423.gif) To further control permissions for fields within the sub-form, you can grant permissions to individual fields. As shown, the Customers collection is configured with individual field permissions (Customer Name is not visible and not editable). ![](https://static-docs.nocobase.com/e7b875521cbc4e28640f027f36d0413c.png) Complete example configuration process: ![](https://static-docs.nocobase.com/7a07e68c2fe2a13f0c2cef19be489264.gif) When the association field component is a sub-table, the situation is consistent with that of a sub-form: As shown, the "Shipment" association field in the Orders collection has all permissions, while the Shipments collection is set to read-only. In the UI, this association field is visible. However, when switched to a sub-table, the fields within the sub-table are visible in the view action but not in the add and edit actions. ![](https://static-docs.nocobase.com/fd4b7d81cdd765db789d9c85cf9dc324.gif) To further control permissions for fields within the sub-table, you can grant permissions to individual fields: ![](https://static-docs.nocobase.com/51d70a624cb2b0366e421bcdc8abb7fd.gif) ### As a Block - The visibility of an association block is controlled by the permissions of the target collection of the corresponding association field, and is independent of the association field's permissions. For example, whether the "Customer" association block is displayed is controlled by the permissions of the Customers collection. ![](https://static-docs.nocobase.com/633ebb301767430b740ecfce11df47b3.gif) - The fields within an association block are controlled by the field permissions in the target collection. As shown, you can set view permissions for individual fields in the Customers collection. ![](https://static-docs.nocobase.com/35af9426c20911323b17f67f81bac8fc.gif) --- url: /users-permissions/acl/union.md --- # Role Union Role Union is a permission management mode. According to system settings, system developers can choose to use `Independent roles`, `Allow roles union`, or `Roles union only`, to meet different permission requirements. ![20250312184651](https://static-docs.nocobase.com/20250312184651.png) ## Independent roles By default, the system uses independent roles. Users must switch between the roles they possess individually. ![20250312184729](https://static-docs.nocobase.com/20250312184729.png) ![20250312184826](https://static-docs.nocobase.com/20250312184826.png) ## Allow roles union System developers can enable `Allow roles union`, allowing users to simultaneously have permissions of all assigned roles while still permitting users to switch roles individually. ![20250312185006](https://static-docs.nocobase.com/20250312185006.png) ## Roles union only Users are enforced to only use Role Union and cannot switch roles individually. ![20250312185105](https://static-docs.nocobase.com/20250312185105.png) ## Rules for Role Union Role union grants the maximum permissions across all roles. Below are the explanations for resolving permission conflicts when roles have different settings on the same permission. ### Operation Permission Merge Example: Role1 is configured to `Allows to configure interface` and Role2 is configured to `Allows to install, activate, disable plugins` ![20250312190133](https://static-docs.nocobase.com/20250312190133.png) ![20250312190352](https://static-docs.nocobase.com/20250312190352.png) When logging in with the **Full Permissions** role, the user will have both permissions simultaneously. ![20250312190621](https://static-docs.nocobase.com/20250312190621.png) ### Data Scope Merge #### Data Rows Scenario 1: Multiple roles setting conditions on the same field Role A filter: Age < 30 | UserID | Name | Age | | ------ | ---- | --- | | 1 | Jack | 23 | | 2 | Lily | 29 | Role B filter: Age > 25 | UserID | Name | Age | | ------ | ---- | --- | | 2 | Lily | 29 | | 3 | Sam | 32 | **After merging:** | UserID | Name | Age | | ------ | ---- | --- | | 1 | Jack | 23 | | 2 | Lily | 29 | | 3 | Sam | 32 | Scenario 2: Different roles setting conditions on different fields Role A filter: Age < 30 | UserID | Name | Age | | ------ | ---- | --- | | 1 | Jack | 23 | | 2 | Lily | 29 | Role B filter: Name contains "Ja" | UserID | Name | Age | | ------ | ------ | --- | | 1 | Jack | 23 | | 3 | Jasmin | 27 | **After merging:** | UserID | Name | Age | | ------ | ------ | --- | | 1 | Jack | 23 | | 2 | Lily | 29 | | 3 | Jasmin | 27 | #### Data Columns Role A visible columns: Name, Age | UserID | Name | Age | | ------ | ---- | --- | | 1 | Jack | 23 | | 2 | Lily | 29 | Role B visible columns: Name, Sex | UserID | Name | Sex | | ------ | ---- | ----- | | 1 | Jack | Man | | 2 | Lily | Woman | **After merging:** | UserID | Name | Age | Sex | | ------ | ---- | --- | ----- | | 1 | Jack | 23 | Man | | 2 | Lily | 29 | Woman | #### Mixed Rows and Columns Role A filter: Age < 30, columns Name, Age | UserID | Name | Age | | ------ | ---- | --- | | 1 | Jack | 23 | | 2 | Lily | 29 | Role B filter: Name contains "Ja", columns Name, Sex | UserID | Name | Sex | | ------ | ----- | ----- | | 3 | Jade | Woman | | 4 | James | Man | **After merging:** | UserID | Name | Age | Sex | | ------ | ----- | ------------------------------------------------ | --------------------------------------------------- | | 1 | Jack | 23 | Man | | 2 | Lily | 29 | Woman | | 3 | Jade | 27 | Woman | | 4 | James | 31 | Man | **Note: Cells with red background indicate data invisible in individual roles but visible in the merged role.** #### Summary Role merging data-scope rules: 1. Between rows, if any condition is satisfied, the row has permissions. 2. Between columns, fields are combined. 3. When rows and columns are both configured, rows and columns are merged separately, not by row-column combinations. --- url: /users-permissions/departments/index.md --- # Departments ## Department Management ![](https://static-docs.nocobase.com/a6eb94a5cc85a6c7b310f33173a5259d.png) ### Create a New Department ![](https://static-docs.nocobase.com/4857910991ae374b63251cee99511e93.png) ![](https://static-docs.nocobase.com/0cd13d99dcd21ced0bb1683557f0b76b.png) ### Create a Sub-Department ![](https://static-docs.nocobase.com/0be8c7db8d12c23f6fe137e7ce23688a.png) ![](https://static-docs.nocobase.com/2db2fc2037ed383edd60117a46fc9dd0.png) ### Edit Department ![](https://static-docs.nocobase.com/a147319577e5cc36b5862c1e511f6722.png) ![](https://static-docs.nocobase.com/f206f866753cf30ac78aadf4f76bad32.png) ### Change Superior Department Modify the superior department field in the department editing form. The current department and its sub-departments are not selectable. ![](https://static-docs.nocobase.com/9d80ddf42f32c77186566ed8ada70128.png) ## Department Member Management ### View Department Member List ![](https://static-docs.nocobase.com/2aaf4d9bf55da105b5fca4e9f7e23ca7.png) ### Add Members to the Department A user can join multiple departments at the same time. The first department they join is the default main department. A user only has one main department. ![](https://static-docs.nocobase.com/60afd282f33b555e6fe0662b9da544cc.png) Users who are already department members will not appear in the user selection list. ![](https://static-docs.nocobase.com/6bcd93173c169973f970de35d2657993.png) ### Set Department Head In the department editing form, click the head field to select department members to become heads. Multiple selections are supported. Members who are already department heads will not appear in the member selection list. ![](https://static-docs.nocobase.com/92970546cbd0aeb5a8b6a36da87583bd.png) ### Configure Departments for Users In addition to adding members to the department, you can also configure departments for users from the user's perspective. ![](https://static-docs.nocobase.com/ca82a802012572e225570e8be93a4094.png) Departments that have already been joined are not selectable. ![](https://static-docs.nocobase.com/70e16d17ee9c4b5d43f8a5e1c633b177.png) ### Change Main Department ![](https://static-docs.nocobase.com/da92dd1e10268adcd35445e9f1dac771.png) ## Search for Users and Departments Search for users by nickname, username, phone, email, and search for departments by department name. ![](https://static-docs.nocobase.com/2d71346a5400205b22436b4db331a9b8.png) --- url: /users-permissions/departments/role.md --- # Department Role Management By associating one or more roles with a department, members of the current department can have these roles. There are two ways to associate roles with a department. The first is to select a role in the role field of the department editing form. ![](https://static-docs.nocobase.com/70f77bb89aa1fb415c152a51a51cc23b.png) The second is to add departments to the corresponding role in role management. ![](https://static-docs.nocobase.com/f2a7bec937cf2f179ce868a92b98416d.png) Departments that already have this role are not selectable. ![](https://static-docs.nocobase.com/be10299893581e1f97a4e01ddd5c7e59.png) --- url: /users-permissions/index.md --- --- url: /users-permissions/sync/dev/resource.md --- # Extending Sync Target Resources ## Overview NocoBase natively supports syncing user data to the **User** and **Department** tables. It also allows for extending the target resources for data synchronization to write data to other tables or perform custom processing as needed. :::warning{title=Experimental} Full documentation is pending. ::: ## Target Resource Handler Interface ```ts export abstract class UserDataResource { name: string; accepts: SyncAccept[]; db: Database; logger: SystemLogger; constructor(db: Database, logger: SystemLogger) { this.db = db; this.logger = logger; } abstract update( record: OriginRecord, resourcePks: PrimaryKey[], matchKey?: string, ): Promise; abstract create( record: OriginRecord, matchKey: string, ): Promise; get syncRecordRepo() { return this.db.getRepository('userDataSyncRecords'); } get syncRecordResourceRepo() { return this.db.getRepository('userDataSyncRecordsResources'); } } ``` ## Registering Target Resources `registerResource(resource: UserDataResource, options?: ToposortOptions)` ```ts import { Plugin } from '@nocobase/server'; import PluginUserDataSyncServer from '@nocobase/plugin-user-data-sync'; class CustomUserResourcePluginServer extends Plugin { async load() { const userDataSyncPlugin = this.app.pm.get(PluginUserDataSyncServer); if (userDataSyncPlugin && userDataSyncPlugin.enabled) { userDataSyncPlugin.resourceManager.registerResource( new CustomDataSyncResource(this.db, this.app.logger), ); } } } ``` --- url: /users-permissions/sync/dev/source.md --- # Extending Synchronized Data Sources ## Overview NocoBase allows users to extend data source types for user data synchronization as needed. ## Server Side ### Data Source Interface The built-in user data synchronization plugin provides registration and management for data source types. To extend a data source type, inherit the `SyncSource` abstract class provided by the plugin and implement the relevant standard interfaces. ```ts import { SyncSource, UserData } from '@nocobase/plugin-user-data-sync'; class CustomSyncSource extends SyncSource { async pull(): Promise { return []; } } ``` The `SyncSource` class includes an `options` property to retrieve custom configurations for the data source. ```ts import { SyncSource, UserData } from '@nocobase/plugin-user-data-sync'; class CustomSyncSource extends SyncSource { async pull(): Promise { //... const { appid, secret } = this.options; //... return []; } } ``` ### Description of `UserData` Fields | Field | Description | | ------------ | ---------------------------------------------- | | `dataType` | Data type, options are `user` and `department` | | `uniqueKey` | Unique identifier field | | `records` | Data records | | `sourceName` | Data source name | If `dataType` is `user`, the `records` field contains the following fields: | Field | Description | | ------------- | ----------------------- | | `id` | User ID | | `nickname` | User nickname | | `avatar` | User avatar | | `email` | Email | | `phone` | Phone number | | `departments` | Array of department IDs | If `dataType` is `department`, the `records` field contains the following fields: | Field | Description | | ---------- | -------------------- | | `id` | Department ID | | `name` | Department name | | `parentId` | Parent department ID | ### Example Implementation of the Data Source Interface ```ts import { SyncSource, UserData } from '@nocobase/plugin-user-data-sync'; class CustomSyncSource extends SyncSource { async pull(): Promise { // ... const ThirdClientApi = new ThirdClientApi( this.options.appid, this.options.secret, ); const departments = await this.clientapi.getDepartments(); const users = await this.clientapi.getUsers(); // ... return [ { dataType: 'department', uniqueKey: 'id', records: departments, sourceName: this.instance.name, }, { dataType: 'user', uniqueKey: 'id', records: users, sourceName: this.instance.name, }, ]; } } ``` ### Registering a Data Source Type The extended data source must be registered with the data management module. ```ts import UserDataSyncPlugin from '@nocobase/plugin-user-data-sync'; class CustomSourcePlugin extends Plugin { async load() { const syncPlugin = this.app.pm.get( UserDataSyncPlugin, ) as UserDataSyncPlugin; if (syncPlugin) { syncPlugin.sourceManager.registerType('custom-source-type', { syncSource: CustomSyncSource, title: 'Custom Source', }); } } } ``` ## Client Side The client user interface registers data source types using the `registerType` method provided by the user data synchronization plugin's client interface: ```ts import SyncPlugin from '@nocobase/plugin-user-data-sync/client'; class CustomSourcePlugin extends Plugin { async load() { const sync = this.app.pm.get(SyncPlugin); sync.registerType(authType, { components: { AdminSettingsForm, // Backend management form }, }); } } ``` ### Backend Management Form ![](https://static-docs.nocobase.com/202412041429835.png) The top section provides general data source configuration, while the bottom section allows for registration of custom configuration forms. --- url: /users-permissions/sync/index.md --- # User Data Synchronization ## Introduction This feature allows you to register and manage user data synchronization sources. By default, an HTTP API is provided, but additional data sources can be supported through plugins. It supports syncing data to the **Users** and **Departments** collections by default, with the possibility to extend synchronization to other target resources using plugins. ## Data Source Management and Synchronization ![](https://static-docs.nocobase.com/202412041043465.png) :::info If no plugins providing user data synchronization sources are installed, user data can be synchronized using the HTTP API. Refer to [Data Source - HTTP API](./sources/api.md). ::: ## Adding a Data Source Once you install a plugin that provides a user data synchronization source, you can add the corresponding data source. Only enabled data sources will display the "Sync" and "Task" buttons. > Example: WeCom ![](https://static-docs.nocobase.com/202412041053785.png) ## Synchronizing Data Click the **Sync** button to start synchronizing data. ![](https://static-docs.nocobase.com/202412041055022.png) Click the **Task** button to view the synchronization status. After successful synchronization, you can view the data in the Users and Departments lists. ![](https://static-docs.nocobase.com/202412041202337.png) For failed synchronization tasks, you can click **Retry**. ![](https://static-docs.nocobase.com/202412041058337.png) In case of synchronization failures, you can troubleshoot the issue through system logs. Additionally, raw synchronization records are stored in the `user-data-sync` directory under the application logs folder. ![](https://static-docs.nocobase.com/202412041205655.png) --- url: /users-permissions/sync/sources/wecom.md --- # Synchronize User Data from WeChat Work ## Introduction The **WeChat Work** plugin supports synchronizing user and department data from WeChat Work. ## Create and configure a custom WeChat Work application. First, you need to create a custom application in the WeChat Work admin console and obtain the **Corp ID**, **AgentId**, and **Secret**. Refer to [User Authentication - WeChat Work](/auth-verification/auth-wecom/). ## Add a synchronization data source in NocoBase Go to Users & Permissions - Sync - Add, and fill in the obtained information. ![](https://static-docs.nocobase.com/202412041251867.png) ## Configure Contacts Sync Go to the WeChat Work admin console - Security and Management - Management Tools, and click on Contacts Sync. ![](https://static-docs.nocobase.com/202412041249958.png) Configure as shown in the figure, and set the trusted IP of the enterprise. ![](https://static-docs.nocobase.com/202412041250776.png) Now you can proceed with user data synchronization. ## Set up the event receiving server If you want changes to user and department data on the WeChat Work side to be synchronized to the NocoBase application in a timely manner, you can proceed with further settings. After filling in the previous configuration information, you can copy the contacts callback notification URL. ![](https://static-docs.nocobase.com/202412041256547.png) Fill it into the WeChat Work settings, obtain the Token and EncodingAESKey, and complete the NocoBase user synchronization data source configuration. ![](https://static-docs.nocobase.com/202412041257947.png) --- url: /users-permissions/user.md --- # Users ## User Management This plugin provides a user management interface in the configuration center. After initializing the application, an undeletable super admin account is automatically created. The super admin has root permissions and can access all resources. ![](https://static-docs.nocobase.com/44bf40f56b45d4dd96c424fb08082cf6.png) In addition, you can also add various different blocks of the Users collection to manage users, such as table blocks: ![](https://static-docs.nocobase.com/76b5a4652f869541a9e8f18a4568a7c9.png) ## Add User ![](https://static-docs.nocobase.com/4f8ef9ffc1c17f275b62b462f6385b19.png) ![](https://static-docs.nocobase.com/437828173950bd7c21b40a6243ffe150.png) ## Modify User Profile ![](https://static-docs.nocobase.com/d25e06872bd1d48ed8c1139728fa5ff3.png) ![](https://static-docs.nocobase.com/c140bcaab240385b9b5aca32a2ec2801.png) ## Change User Password ![](https://static-docs.nocobase.com/26c24c4cebda3d144dc4e9b728c2ede5.png) ![](https://static-docs.nocobase.com/23a2b2223cb5b387b3699cc6143302e8.png) ## Settings :::info{title=Tip} Requires NocoBase v1.3.34-beta or later. ::: Configure whether to allow editing user profiles and changing user passwords (applies to all users). ![20241021212438](https://static-docs.nocobase.com/20241021212438.png) ![20241021212659](https://static-docs.nocobase.com/20241021212659.png) --- url: /workflow/advanced/executions.md --- # Execution Plan (History) After a workflow is triggered, a corresponding execution plan is created to track the execution process of this task. Each execution plan has a status value to indicate the current execution status, which can be viewed in the list and details of the execution history: ![Execution Plan Status](https://static-docs.nocobase.com/d4440d92ccafac6fac85da4415bb2a26.png) When all nodes in the main process branch are executed to the end of the process with a "Completed" status, the entire execution plan will end with a "Completed" status. When a node in the main process branch has a final status such as "Failed", "Error", "Cancelled", or "Rejected", the entire execution plan will be **terminated prematurely** with the corresponding status. When a node in the main process branch has a "Waiting" status, the entire execution plan will be paused, but will still show a "Running" status, until the waiting node is resumed. Different node types handle the waiting state differently. For example, a manual node needs to wait for manual processing, while a delay node needs to wait for the specified time to pass before continuing. The statuses of an execution plan are as follows: | Status | Corresponding status of the last executed node in the main process | Meaning | | ------ | ---------------------------- | ------------------------------------------------ | | Queued | - | The workflow has been triggered and an execution plan has been generated, waiting in the queue for the scheduler to arrange execution. | | Running | Waiting | The node requires a pause, waiting for further input or a callback to continue. | | Completed | Completed | No issues were encountered, and all nodes were executed one by one as expected. | | Failed | Failed | Failed because the node configuration was not met. | | Error | Error | The node encountered an unhandled program error and terminated prematurely. | | Cancelled | Cancelled | A waiting node was cancelled externally by the workflow administrator, terminating prematurely. | | Rejected | Rejected | In a manual processing node, it was manually rejected, and the subsequent process will not continue. | In the [Quick Start](../getting-started.md) example, we already know that by viewing the details of a workflow's execution history, we can check whether all nodes were executed normally, as well as the execution status and result data of each executed node. In some advanced workflows and nodes, a node may have multiple results, such as the result of a loop node: ![Node results from multiple executions](https://static-docs.nocobase.com/bbda259fa2ddf62b0fc0f982efbedae9.png) :::info{title=Tip} Workflows can be triggered concurrently, but they are executed sequentially in a queue. Even if multiple workflows are triggered at the same time, they will be executed one by one, not in parallel. Therefore, a "Queued" status means that other workflows are currently running and it needs to wait. The "Running" status only indicates that the execution plan has started and is usually paused due to the waiting state of an internal node. It does not mean that this execution plan has preempted the execution resources at the head of the queue. Therefore, when there is a "Running" execution plan, other "Queued" execution plans can still be scheduled to start. ::: ## Node Execution Status The status of an execution plan is determined by the execution of each of its nodes. In an execution plan after a trigger, each node produces an execution status after it runs, and this status determines whether the subsequent process will continue. Normally, after a node executes successfully, the next node will be executed, until all nodes are executed in sequence or the process is interrupted. When encountering flow control-related nodes, such as branches, loops, parallel branches, delays, etc., the execution flow to the next node is determined based on the conditions configured in the node and the runtime context data. The possible statuses of a node after execution are as follows: | Status | Is Final State | Terminates Prematurely | Meaning | | ---- | :--------: | :----------: | ------------------------------------------------------ | | Waiting | No | No | The node requires a pause, waiting for further input or a callback to continue. | | Completed | Yes | No | No issues were encountered, executed successfully, and continues to the next node until the end. | | Failed | Yes | Yes | Failed because the node configuration was not met. | | Error | Yes | Yes | The node encountered an unhandled program error and terminated prematurely. | | Cancelled | Yes | Yes | A waiting node was cancelled externally by the workflow administrator, terminating prematurely. | | Rejected | Yes | Yes | In a manual processing node, it was manually rejected, and the subsequent process will not continue. | Except for the "Waiting" status, all other statuses are final states for node execution. Only when the final state is "Completed" will the process continue; otherwise, the entire workflow execution will be terminated prematurely. When a node is in a branch flow (parallel branch, condition, loop, etc.), the final state produced by the node's execution will be handled by the node that initiated the branch, and this determines the flow of the entire workflow. For example, when we use a condition node in "'Yes' to continue" mode, if the result is "No" during execution, the entire workflow will be terminated prematurely with a "Failed" status, and subsequent nodes will not be executed, as shown in the figure below: ![Node execution failed](https://static-docs.nocobase.com/993aecfa1465894bb574444f0a44313e.png) :::info{title=Tip} All terminating statuses other than "Completed" can be considered failures, but the reasons for failure are different. You can view the node's execution results to further understand the cause of the failure. ::: --- url: /workflow/advanced/options.md --- # Advanced Configuration ## Execution Mode Workflows are executed either asynchronously or synchronously, based on a trigger type selected during creation. Asynchronous mode means that after a specific event is triggered, the workflow enters a queue and is executed one by one by background scheduling. Synchronous mode, on the other hand, does not enter the scheduling queue after being triggered; it starts executing directly and provides immediate feedback upon completion. Collection events, after action events, custom action events, scheduled events, and approval events are executed asynchronously by default. Before action events are executed synchronously by default. Both collection events and form events support both modes, which can be selected when creating a workflow: ![Sync Mode_Create Sync Workflow](https://static-docs.nocobase.com/39bc0821f50c1bde4729c531c6236795.png) :::info{title=Note} Due to their nature, synchronous workflows cannot use nodes that produce a "waiting" state, such as "Manual processing". ::: ## Auto-delete Execution History When a workflow is triggered frequently, you can configure auto-deletion of execution history to reduce clutter and alleviate storage pressure on the database. You can also configure whether to automatically delete the execution history for a workflow in its creation and editing dialogs: ![Auto-delete Execution History Configuration](https://static-docs.nocobase.com/b2e4c08e7a01e213069912fe04baa7bd.png) Auto-deletion can be configured based on the execution result status. In most cases, it is recommended to only check the "Completed" status to preserve records of failed executions for future troubleshooting. It is recommended not to enable auto-deletion of execution history when debugging a workflow, so you can use the history to check if the workflow's execution logic is as expected. :::info{title=Note} Deleting a workflow's history does not reduce its execution count. ::: --- url: /workflow/advanced/revisions.md --- # Version Management After a configured workflow has been triggered at least once, if you want to modify the workflow's configuration or its nodes, you need to create a new version before making changes. This also ensures that when reviewing the execution history of previously triggered workflows, it will not be affected by future modifications. On the workflow configuration page, you can view existing workflow versions from the version menu in the upper right corner: ![View workflow versions](https://static-docs.nocobase.com/ad93d2c08166b0e3e643fb148713a63f.png) In the more actions ("...") menu to its right, you can choose to copy the currently viewed version to a new version: ![Copy workflow to a new version](https://static-docs.nocobase.com/2805798e6caca2af004893390a744256.png) After copying to a new version, click the "Enable"/"Disable" toggle to switch the corresponding version to the enabled state, and the new workflow version will take effect. If you need to re-select an old version, switch to it from the version menu, then click the "Enable"/"Disable" toggle again to switch it to the enabled state. The currently viewed version will take effect, and subsequent triggers will execute the process of that version. When you need to disable the workflow, click the "Enable"/"Disable" toggle to switch it to the disabled state, and the workflow will no longer be triggered. :::info{title=Tip} Unlike "Copying" a workflow from the workflow management list, a workflow "copied to a new version" is still grouped under the same workflow set, just distinguished by version. However, copying a workflow is treated as a completely new workflow, unrelated to the versions of the previous workflow, and its execution count will also be reset to zero. ::: --- url: /workflow/advanced/variables.md --- # Using Variables ## Core Concepts Just like variables in a programming language, **variables** in a workflow are an important tool for connecting and organizing processes. When each node is executed after a workflow is triggered, some configuration items can use variables. The source of these variables is the data from the upstream nodes of the current node, including the following categories: - Trigger context data: In cases like action triggers or collection triggers, a single row data object can be used as a variable by all nodes. The specifics vary depending on the implementation of each trigger. - Upstream node data: When the process reaches any node, it's the result data of previously completed nodes. - Local variables: When a node is within some special branch structures, it can use specific local variables within that branch. For example, in a loop structure, the data object of each iteration can be used. - System variables: Some built-in system parameters, such as the current time. We have used the variable feature multiple times in [Getting Started](../getting-started.md). For example, in a calculation node, we can use variables to reference trigger context data for calculations: ![Calculation node using functions and variables](https://static-docs.nocobase.com/837e4851a4c70a1932542caadef3431b.png) In an update node, use the trigger context data as a variable for the filter condition, and reference the result of the calculation node as a variable for the field value to be updated: ![Update data node variables](https://static-docs.nocobase.com/2e147c93643e7ebc709b9b7ab4f3af8c.png) ## Data Structure A variable is internally a JSON structure, and you can usually use a specific part of the data by its JSON path. Since many variables are based on NocoBase's collection structure, association data will be structured hierarchically as object properties, forming a tree-like structure. For example, we can select the value of a specific field from the association data of the queried data. Additionally, when the association data has a to-many structure, the variable may be an array. When selecting a variable, you will most often need to select the last-level value attribute, which is usually a simple data type like a number or a string. However, when there is an array in the variable hierarchy, the last-level attribute will also be mapped to an array. Only if the corresponding node supports arrays can the array data be processed correctly. For example, in a calculation node, some calculation engines have functions specifically for handling arrays. Another example is in a loop node, where the loop object can also be an array. For example, when a query node queries multiple pieces of data, the node result will be an array containing multiple rows of homogeneous data: ```json [ { "id": 1, "title": "Title 1" }, { "id": 2, "title": "Title 2" } ] ``` However, when using it as a variable in subsequent nodes, if the selected variable is in the form of `Node data/Query node/Title`, you will get an array mapped to the corresponding field values: ```json ["Title 1", "Title 2"] ``` If it is a multi-dimensional array (such as a many-to-many association field), you will get a one-dimensional array with the corresponding field flattened. ## System Built-in Variables ### System Time Gets the system time at the moment the node is executed. The time zone of this time is the one set on the server. ### Date Range Parameters Can be used when configuring date field filter conditions in query, update, and delete nodes. It is only supported for "is" comparisons. Both the start and end times of the date range are based on the time zone set on the server. ![Date range parameters](https://static-docs.nocobase.com/20240817175354.png) --- url: /workflow/getting-started.md --- # Getting Started ## Configure Your First Workflow Go to the workflow plugin management page from the plugin configuration menu in the top menu bar: ![Workflow plugin management entry](https://static-docs.nocobase.com/20251027222721.png) The management interface lists all created workflows: ![Workflow Management](https://static-docs.nocobase.com/20251027222900.png) Click the "Add New" button to create a new workflow and select Collection Event: ![Create Workflow](https://static-docs.nocobase.com/20251027222951.png) After submitting, click the "Configure" link in the list to enter the workflow configuration interface: ![An empty workflow](https://static-docs.nocobase.com/20251027223131.png) Then, click the trigger card to open the trigger configuration drawer. Select a previously created collection (e.g., "Posts"), choose "After record added" for the trigger timing, and click the "Save" button to complete the trigger configuration: ![Configure Trigger](https://static-docs.nocobase.com/20251027224735.png) Next, we can click the plus button in the flow to add a node. For example, select a calculation node to concatenate the "Title" field and the "ID" field from the trigger data: ![Add Calculation Node](https://static-docs.nocobase.com/20251027224842.png) Click the node card to open the node configuration drawer. Use the `CONCATENATE` function provided by Formula.js to concatenate the "Title" and "ID" fields. The two fields are inserted through the variable selector: ![Calculation node using functions and variables](https://static-docs.nocobase.com/20251027224939.png) Then, create an update record node to save the result to the "Title" field: ![Create Update Record Node](https://static-docs.nocobase.com/20251027232654.png) Similarly, click the card to open the update record node's configuration drawer. Select the "Posts" collection, choose the data ID from the trigger for the record ID to update, select "Title" for the field to update, and choose the result from the calculation node for the value: ![Configure Update Record Node](https://static-docs.nocobase.com/20251027232802.png) Finally, click the "Enable"/"Disable" switch in the upper-right toolbar to switch the workflow to the enabled state, so that the workflow can be triggered and executed. ## Triggering the Workflow Return to the main system interface, create a post through the posts block, and fill in the post title: ![Create post data](https://static-docs.nocobase.com/20251027233004.png) After submitting and refreshing the block, you can see that the post title has been automatically updated to the format "Post Title + Post ID": ![Post title modified by workflow](https://static-docs.nocobase.com/20251027233043.png) :::info{title=Tip} Since workflows triggered by collection events are executed asynchronously, you cannot see the data update immediately in the interface after submitting the data. However, after a short while, you can see the updated content by refreshing the page or block. ::: ## Viewing Execution History The workflow has just been successfully triggered and executed once. We can go back to the workflow management interface to view the corresponding execution history: ![View workflow list](https://static-docs.nocobase.com/20251027233246.png) In the workflow list, you can see that this workflow has generated one execution history. Click the link in the count column to open the execution history records for the corresponding workflow: ![Execution history list for the corresponding workflow](https://static-docs.nocobase.com/20251027233341.png) Click the "View" link to enter the details page for that execution, where you can see the execution status and result data for each node: ![Workflow execution history details](https://static-docs.nocobase.com/20251027233615.png) The context data of the trigger and the result data of the node execution can be viewed by clicking the status button in the upper right corner of the corresponding card. For example, let's view the result data of the calculation node: ![Calculation node result](https://static-docs.nocobase.com/20251027233635.png) You can see that the result data of the calculation node contains the calculated title, which is the data used by the subsequent update record node. ## Summary Through the steps above, we have completed the configuration and triggering of a simple workflow and have been introduced to the following basic concepts: - **Workflow**: Used to define the basic information of a flow, including name, trigger type, and enabled status. You can configure any number of nodes within it. It is the entity that carries the flow. - **Trigger**: Each workflow contains one trigger, which can be configured with specific conditions for the workflow to be triggered. It is the entry point of the flow. - **Node**: A node is an instruction unit within a workflow that performs a specific action. Multiple nodes in a workflow form a complete execution flow through upstream and downstream relationships. - **Execution**: An execution is the specific execution object after a workflow is triggered, also known as an execution record or execution history. It contains information such as the execution status and trigger context data. There are also corresponding execution results for each node, which include the node's execution status and result data information. For more in-depth usage, you can refer to the following content: - [Triggers](./triggers/index) - [Nodes](./nodes/index) - [Using Variables](./advanced/variables) - [Executions](./advanced/executions) - [Version Management](./advanced/revisions) - [Advanced Options](./advanced/options) --- url: /workflow/index.md --- # Overview ## Introduction The Workflow plugin helps you orchestrate automated business processes in NocoBase, such as daily approvals, data synchronization, reminders, and other tasks. In a workflow, you can implement complex business logic by simply configuring triggers and related nodes through a visual interface, without writing any code. ### Example Each workflow is orchestrated with a trigger and several nodes. The trigger represents an event in the system, and each node represents an execution step. Together, they describe the business logic to be processed after the event occurs. The following image shows a typical inventory deduction process after a product order is placed: ![Workflow Example](https://static-docs.nocobase.com/20251029222146.png) When a user submits an order, the workflow automatically checks the inventory. If the inventory is sufficient, it deducts the stock and proceeds with order creation; otherwise, the process ends. ### Use Cases From a more general perspective, workflows in NocoBase applications can solve problems in various scenarios: - Automate repetitive tasks: Order reviews, inventory synchronization, data cleansing, score calculations, etc., no longer require manual operation. - Support human-machine collaboration: Arrange for approvals or reviews at key nodes, and continue with subsequent steps based on the results. - Connect to external systems: Send HTTP requests, receive pushes from external services, and achieve cross-system automation. - Quickly adapt to business changes: Adjust the process structure, conditions, or other node configurations, and go live without a new release. ## Installation Workflow is a built-in plugin of NocoBase. No additional installation or configuration required. ## Learn More - [Getting Started](./getting-started) - [Triggers](./triggers/index) - [Nodes](./nodes/index) - [Using Variables](./advanced/variables) - [Executions](./advanced/executions) - [Version Management](./advanced/revisions) - [Advanced Configuration](./advanced/options) - [Extension Development](./development/index) --- url: /workflow/nodes/aggregate.md --- # Aggregate Query ## Introduction Used to perform aggregate function queries on data in a collection that meets certain conditions, and returns the corresponding statistical results. It is often used to process statistical data for reports. The node's implementation is based on database aggregate functions. Currently, it only supports statistics on a single field of a collection. The numerical result of the statistics will be saved in the node's output for use by subsequent nodes. ## Installation Built-in plugin, no installation required. ## Create Node In the workflow configuration interface, click the plus (+) button in the flow to add an "Aggregate Query" node: ![Create Aggregate Query Node](https://static-docs.nocobase.com/7f9d806ebf5064f80c30f8b67f316f0f.png) ## Node Configuration ![Aggregate Query Node_Node Configuration](https://static-docs.nocobase.com/57362f747b9992230567c6bb5e986fd2.png) ### Aggregate Function Supports 5 aggregate functions from SQL: `COUNT`, `SUM`, `AVG`, `MIN`, and `MAX`. Select one of them to perform an aggregate query on the data. ### Target Type The target of the aggregate query can be selected in two modes. One is to directly select the target collection and one of its fields. The other is to select its one-to-many related collection and field through an existing data object in the workflow context to perform the aggregate query. ### Distinct This is the `DISTINCT` in SQL. The field for deduplication is the same as the selected collection field. Selecting different fields for these two is not currently supported. ### Filter Conditions Similar to the filter conditions in a normal collection query, you can use context variables from the workflow. ## Example The aggregate target "Collection data" is relatively easy to understand. Here, we will use "counting the total number of articles in a category after a new article is added" as an example to introduce the usage of the aggregate target "Associated collection data". First, create two collections: "Articles" and "Categories". The Articles collection has a many-to-one relationship field pointing to the Categories collection, and a reverse one-to-many relationship field is also created from Categories to Articles: | Field Name | Type | | ---------- | ---------------------- | | Title | Single Line Text | | Category | Many-to-One (Categories) | | Field Name | Type | | ------------- | -------------------- | | Category Name | Single Line Text | | Articles | One-to-Many (Articles) | Next, create a workflow triggered by a collection event. Select it to trigger after new data is added to the Articles collection. Then, add an aggregate query node and configure it as follows: ![Aggregate Query Node_Example_Node Configuration](https://static-docs.nocobase.com/542272e638c6c0a567373d1b37ddda78.png) This way, after the workflow is triggered, the aggregate query node will count the number of all articles in the category of the newly added article and save it as the node's result. :::info{title=Tip} If you need to use the relationship data from the collection event trigger, you must configure the relevant fields in the "Preload associated data" section of the trigger, otherwise it cannot be selected. ::: --- url: /workflow/nodes/approval.md --- # Approval ## Introduction In an approval workflow, a dedicated "Approval" node is required to configure the operational logic for approvers to process (pass, reject, or return) the initiated approval. The "Approval" node can only be used in approval processes. :::info{title=Tip} Difference from the regular "Manual" node: The regular "Manual" node is for more general scenarios, such as manual data input or manual decisions on whether to continue the process in various types of workflows. The "Approval" node is a specialized processing node exclusively for approval processes, handling only the data of the initiated approval, and cannot be used in other workflows. ::: ## Create Node Click the plus ("+") button in the process, add an "Approval" node, and then select one of the pass modes to create the approval node: ![Create Approval Node](https://static-docs.nocobase.com/20251107000938.png) ## Node Configuration ### Pass Mode There are two pass modes: 1. **Pass-through mode**: Typically used for simpler processes. Whether the approval node passes or not only determines if the process ends. If it doesn't pass, the process exits directly. ![Pass-through Mode](https://static-docs.nocobase.com/20251107001043.png) 2. **Branch mode**: Typically used for more complex data logic. After the approval node produces any result, other nodes can continue to be executed within its result branch. ![Branch Mode](https://static-docs.nocobase.com/20251107001234.png) After this node is "Passed", in addition to executing the pass branch, the subsequent process will also continue. After a "Reject" action, the subsequent process can also continue by default, or you can configure the node to end the process after executing the branch. :::info{title=Tip} The pass mode cannot be modified after the node is created. ::: ### Approver The approver is the set of users responsible for the approval action of this node. It can be one or more users. The source can be a static value selected from the user list, or a dynamic value specified by a variable: ![Approver Configuration](https://static-docs.nocobase.com/20251107001433.png) When selecting a variable, you can only choose the primary key or foreign key of user data from the context and node results. If the selected variable is an array during execution (a to-many relationship), each user in the array will be merged into the entire approver set. In addition to directly selecting users or variables, you can also dynamically filter users who meet the criteria as approvers based on the query conditions of the user collection: ![Dynamic Filter Approvers](https://static-docs.nocobase.com/20251107001703.png) ### Agreement Mode If there is only one approver at the time of final execution (including the case after deduplicating multiple variables), regardless of the agreement mode selected, only that user will perform the approval operation, and the result will be determined solely by that user. When there are multiple users in the approver set, selecting different agreement modes represents different processing methods: 1. **Anyone**: If any one person passes, the node passes. The node is rejected only if everyone rejects. 2. **Countersign**: Everyone must pass for the node to pass. If any one person rejects, the node is rejected. 3. **Vote**: The number of people who pass must exceed a set ratio for the node to pass; otherwise, the node is rejected. For the return action, in any mode, if any user in the approver set processes it as a return, the node will directly exit the process. ### Processing Order Similarly, when there are multiple users in the approver set, selecting different processing orders represents different processing methods: 1. **Parallel**: All approvers can process in any order; the sequence of processing does not matter. 2. **Sequential**: Approvers process sequentially according to the order in the approver set. The next approver can only process after the previous one has submitted. Regardless of whether it is set to "Sequential" processing, the result produced according to the actual processing order will also follow the rules in the "Agreement Mode" mentioned above. The node completes its execution once the corresponding conditions are met. ### Exit workflow after the reject branch ends When "Pass Mode" is set to "Branch mode", you can choose to exit the workflow after the reject branch ends. After checking this option, a "✗" will be displayed at the end of the reject branch, indicating that subsequent nodes will not continue after this branch ends: ![Exit after Reject](https://static-docs.nocobase.com/20251107001839.png) ### Approver Interface Configuration The approver interface configuration is used to provide an operation interface for the approver when the approval workflow executes to this node. Click the configure button to open the pop-up window: ![Approver Interface Configuration Popup](https://static-docs.nocobase.com/20251107001958.png) In the configuration pop-up, you can add blocks such as original submission content, approval information, processing form, and custom prompt text: ![Add Blocks to Approver Interface](https://static-docs.nocobase.com/20251107002604.png) #### Original Submission Content The approval content details block is the data block submitted by the initiator. Similar to a regular data block, you can add field components from the collection and arrange them freely to organize the content that the approver needs to view: ![Details Block Configuration](https://static-docs.nocobase.com/20251107002925.png) #### Processing Form In the action form block, you can add action buttons supported by this node, including "Pass", "Reject", "Return", "Reassign", and "Add Signer": ![Action Form Block](https://static-docs.nocobase.com/20251107003015.png) Additionally, fields that can be modified by the approver can also be added to the action form. These fields will be displayed in the action form when the approver is processing the approval. The approver can modify the values of these fields, and upon submission, both the data for approval and the snapshot of the corresponding data in the approval process will be updated simultaneously. ![Modify Approval Content Fields](https://static-docs.nocobase.com/20251107003206.png) #### "Pass" and "Reject" Among the approval action buttons, "Pass" and "Reject" are decisive actions. After submission, the approver's processing for this node is complete. Additional fields that need to be filled out upon submission, such as "Comment", can be added in the "Processing Configuration" pop-up for the action button. ![Processing Configuration](https://static-docs.nocobase.com/20251107003314.png) #### "Return" "Return" is also a decisive action. In addition to configuring comments, you can also configure the nodes to which it can be returned: ![Return Configuration](https://static-docs.nocobase.com/20251107003555.png) #### "Reassign" and "Add Signer" "Reassign" and "Add Signer" are non-decisive actions used to dynamically adjust the approvers in the approval process. "Reassign" is to hand over the current user's approval task to another user for processing. "Add Signer" is to add an approver before or after the current approver, and the new approver will continue the approval process together. After enabling the "Reassign" or "Add Signer" action buttons, you need to select the "Assignment Scope" in the button's configuration menu to set the range of users who can be assigned as new approvers: ![Assignment Scope](https://static-docs.nocobase.com/20241226232321.png) Same as the original approver configuration of the node, the assignment scope can also be directly selected approvers or based on query conditions of the user collection. It will eventually be merged into a set and will not include users already in the approver set. :::warning{title=Important} If an action button is enabled or disabled, or the assignment scope is modified, you must save the node's configuration after closing the action interface configuration pop-up. Otherwise, the changes to the action button will not take effect. ::: ### "My Approvals" Card 2.0+ Can be used to configure the task card in the "My Approvals" list in the To-do Center. ![My Approvals Card Configuration](https://static-docs.nocobase.com/20260214141554.png) The card can be freely configured to display the desired business fields (excluding association fields) or approval-related information. After the approval enters this node, the customized task card will be visible in the To-do Center list: ![To-do Center Task Card](https://static-docs.nocobase.com/20260214141722.png) ## Node Result After the approval is completed, the relevant status and data will be recorded in the node result and can be used as variables by subsequent nodes. ![Node Result](https://static-docs.nocobase.com/20250614095052.png) ### Node Approval Status Represents the processing status of the current approval node. The result is an enumerated value. ### Data After Approval If the approver modifies the approval content in the action form, the modified data will be recorded in the node result for use by subsequent nodes. To use association fields, you need to configure preloading for the association fields in the trigger. ### Approval Records > v1.8.0+ The approval processing record is an array that contains the processing records of all approvers in this node. Each processing record includes the following fields: | Field | Type | Description | | --- | --- | --- | | id | number | Unique identifier for the processing record | | userId | number | User ID who processed this record | | status | number | Processing status | | comment | string | Comment at the time of processing | | updatedAt | string | Update time of the processing record | You can use these fields as variables in subsequent nodes as needed. --- url: /workflow/nodes/calculation.md --- # Calculation The Calculation node can evaluate an expression, and the result is saved in the result of the corresponding node for use by subsequent nodes. It is a tool for calculating, processing, and transforming data. To some extent, it can replace the function in programming languages of calling a function on a value and assigning it to a variable. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Calculation" node: ![Calculation Node_Add](https://static-docs.nocobase.com/58a455540d26945251cd143eb4b16579.png) ## Node Configuration ![Calculation Node_Configuration](https://static-docs.nocobase.com/6a155de3f6a883d8cd1881b2d9c33874.png) ### Calculation Engine The calculation engine defines the syntax supported by the expression. The currently supported calculation engines are [Math.js](https://mathjs.org/) and [Formula.js](https://formulajs.info/). Each engine has a large number of built-in common functions and methods for data operations. For specific usage, you can refer to their official documentation. :::info{title=Tip} It should be noted that different engines differ in array index access. Math.js indices start from `1`, while Formula.js starts from `0`. ::: In addition, if you need simple string concatenation, you can directly use "String Template". This engine will replace the variables in the expression with their corresponding values and then return the concatenated string. ### Expression An expression is a string representation of a calculation formula, which can consist of variables, constants, operators, and supported functions. You can use variables from the flow context, such as the result of a preceding node of the calculation node, or local variables of a loop. If the expression input does not conform to the syntax, an error will be prompted in the node configuration. If a variable does not exist or the type does not match during execution, or if a non-existent function is used, the calculation node will terminate prematurely with an error status. ## Example ### Calculate Order Total Price An order may contain multiple items, and each item has a different price and quantity. The total price of the order needs to be the sum of the products of the price and quantity of all items. After loading the list of order details (a one-to-many relationship dataset), you can use a calculation node to calculate the total price of the order: ![Calculation Node_Example_Configuration](https://static-docs.nocobase.com/85966b0116afb49aa966eeaa85e78dae.png) Here, the `SUMPRODUCT` function from Formula.js can calculate the sum of products for two arrays of the same length, which yields the total price of the order. --- url: /workflow/nodes/cc.md --- # Carbon Copy v1.8.2+ ## Introduction The Carbon Copy node is used to send certain contextual content from the workflow execution process to specified users for their information and review. For example, in an approval or other process, relevant information can be carbon copied to other participants so they can stay informed about the progress. You can set up multiple Carbon Copy nodes in a workflow. When the workflow execution reaches a node, the relevant information will be sent to the specified recipients. The carbon copied content will be displayed in the "CC to Me" menu of the To-Do Center, where users can view all content carbon copied to them. It will also indicate unread items, and users can manually mark them as read after viewing. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Carbon Copy" node: ![Add Carbon Copy](https://static-docs.nocobase.com/20250710222842.png) ## Node Configuration ![Node Configuration](https://static-docs.nocobase.com/20250710224041.png) In the node configuration interface, you can set the following parameters: ### Recipients Recipients are the collection of target users for the carbon copy, which can be one or more users. The source can be a static value selected from the user list, a dynamic value specified by a variable, or the result of a query on the users collection. ![Recipient Configuration](https://static-docs.nocobase.com/20250710224421.png) ### User Interface Recipients need to view the carbon copied content in the "CC to Me" menu of the To-Do Center. You can configure the results of the trigger and any node in the workflow context as content blocks. ![User Interface](https://static-docs.nocobase.com/20250710225400.png) ### Task Card 2.0+ Can be used to configure the task card in the "CC to Me" list in the To-Do Center. ![20260213010947](https://static-docs.nocobase.com/20260213010947.png) The card can be freely configured to display the desired business fields (excluding association fields). After the workflow carbon copy task is created, the customized task card will be visible in the To-Do Center list: ![20260214124325](https://static-docs.nocobase.com/20260214124325.png) ### Task Title The task title is the title displayed in the To-Do Center. You can use variables from the workflow context to dynamically generate the title. ![Task Title](https://static-docs.nocobase.com/20250710225603.png) ## To-Do Center Users can view and manage all content carbon copied to them in the To-Do Center, and filter and view based on read status. ![20250710232932](https://static-docs.nocobase.com/20250710232932.png) ![20250710233032](https://static-docs.nocobase.com/20250710233032.png) After viewing, you can mark it as read, and the unread count will decrease accordingly. ![20250710233102](https://static-docs.nocobase.com/20250710233102.png) --- url: /workflow/nodes/condition.md --- # Condition ## Introduction Similar to the `if` statement in programming languages, it determines the subsequent flow direction based on the result of the configured condition. ## Create Node The Condition node has two modes: "Continue if true" and "Branch on true/false". You must select one of these modes when creating the node, and it cannot be changed in the node's configuration afterward. ![Condition_Mode_Selection](https://static-docs.nocobase.com/3de27308c1179523d8606c66bf3a5fb4.png) In the "Continue if true" mode, when the condition's result is "true", the workflow will continue to execute subsequent nodes. Otherwise, the workflow will terminate and exit prematurely with a failed status. !["Continue if true" mode](https://static-docs.nocobase.com/0f6ae1afe61d501f8eb1f6dedb3d4ad7.png) This mode is suitable for scenarios where the workflow should not proceed if the condition is not met. For example, a form submission button for submitting an order is bound to a "Before action event". If the stock for the product in the order is insufficient, the order creation process should not continue but should fail and exit. In the "Branch on true/false" mode, the condition node will create two subsequent branches, corresponding to the "true" and "false" results of the condition. Each branch can be configured with its own subsequent nodes. After either branch completes its execution, it will automatically merge back into the parent branch of the condition node to continue executing the following nodes. !["Branch on true/false" mode](https://static-docs.nocobase.com/974a1fcd8603629b64ffce6c55d59282.png) This mode is suitable for scenarios where different actions need to be performed depending on whether the condition is met or not. For example, checking if a piece of data exists: if it doesn't, create it; if it does, update it. ## Node Configuration ### Calculation Engine Currently, three engines are supported: - **Basic**: Obtains a logical result through simple binary calculations and "AND"/"OR" grouping. - **Math.js**: Calculates expressions supported by the [Math.js](https://mathjs.org/) engine to obtain a logical result. - **Formula.js**: Calculates expressions supported by the [Formula.js](https://formulajs.info/) engine to obtain a logical result. In all three calculation types, variables from the workflow context can be used as parameters. --- url: /workflow/nodes/create.md --- # Create Record Used to add a new record to a collection. The field values for the new record can use variables from the workflow context. To assign values to association fields, you can directly reference the corresponding data variables in the context, which can be either an object or a foreign key value. If not using variables, you need to manually enter the foreign key values. For multiple foreign key values in a to-many relationship, they must be separated by commas. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Create Record" node: ![Add 'Create Record' node](https://static-docs.nocobase.com/386c8c01c89b1eeab848510e77f4841a.png) ## Node Configuration ![Create Record Node_Example_Node Configuration](https://static-docs.nocobase.com/5f7b97a51b64a1741cf82a4d4455b610.png) ### Collection Select the collection to which you want to add a new record. ### Field Values Assign values to the fields of the collection. You can use variables from the workflow context or manually enter static values. :::info{title="Note"} Data created by the "Create Record" node in a workflow does not automatically handle user data like "Created by" and "Last modified by". You need to configure the values for these fields yourself as needed. ::: ### Preload association data If the fields of the new record include association fields and you want to use the corresponding association data in subsequent workflow steps, you can check the corresponding association fields in the preload configuration. This way, after the new record is created, the corresponding association data will be automatically loaded and stored together in the node's result data. ## Example For example, when a record in the "Posts" collection is created or updated, a "Post Versions" record needs to be automatically created to log a change history for the post. You can use the "Create Record" node to achieve this: ![Create Record Node_Example_Workflow Configuration](https://static-docs.nocobase.com/dfd4820d49c145fa331883fc09c9161f.png) ![Create Record Node_Example_Node Configuration](https://static-docs.nocobase.com/1a0992e66170be12a068da6503298868.png) After enabling the workflow with this configuration, when a record in the "Posts" collection is changed, a "Post Versions" record will be automatically created to log the change history of the post. --- url: /workflow/nodes/date-calculation.md --- # Date Calculation ## Introduction The Date Calculation node provides nine calculation functions, including adding a time period, subtracting a time period, formatted output of a time string, and duration unit conversion. Each function has specific input and output value types, and can also receive results from other nodes as parameter variables. It uses a calculation pipeline to chain the results of configured functions to finally obtain an expected output. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Date Calculation" node: ![Date Calculation Node_Create Node](https://static-docs.nocobase.com/[图片].png) ## Node Configuration ![Date Calculation Node_Node Configuration](https://static-docs.nocobase.com/20240817184423.png) ### Input Value The input value can be a variable or a date constant. The variable can be the data that triggered this workflow or the result of an upstream node in this workflow. For the constant, you can select any date. ### Input Value Type Refers to the type of the input value. There are two possible values. * Date type: Means the input value can ultimately be converted to a date-time type, such as a numeric timestamp or a string representing time. * Number type: Since the input value type affects the selection of the following time calculation steps, it is necessary to correctly select the input value type. ### Calculation Steps Each calculation step consists of a calculation function and its parameter configuration. It adopts a pipeline design, where the result from the previous function's calculation serves as the input value for the next function's calculation. In this way, a series of time calculations and conversions can be completed. After each calculation step, the output type is also fixed and will affect the functions available for the next calculation step. The calculation can only continue if the types match. Otherwise, the result of a step will be the final output of the node. ## Calculation Functions ### Add a period of time - Receives input value type: Date - Parameters - The amount to add, which can be a number or a built-in variable from the node. - Time unit. - Output value type: Date - Example: When the input value is `2024-7-15 00:00:00`, the amount is `1`, and the unit is "day", the calculation result is `2024-7-16 00:00:00`. ### Subtract a period of time - Receives input value type: Date - Parameters - The amount to subtract, which can be a number or a built-in variable from the node. - Time unit. - Output value type: Date - Example: When the input value is `2024-7-15 00:00:00`, the amount is `1`, and the unit is "day", the calculation result is `2024-7-14 00:00:00`. ### Calculate the difference with another time - Receives input value type: Date - Parameters - The date to calculate the difference with, which can be a date constant or a variable from the workflow context. - Time unit. - Whether to take the absolute value. - Rounding operation: Options include keeping decimals, rounding, rounding up, and rounding down. - Output value type: Number - Example: When the input value is `2024-7-15 00:00:00`, the comparison object is `2024-7-16 06:00:00`, the unit is "day", absolute value is not taken, and decimals are kept, the calculation result is `-1.25`. :::info{title=Tip} When absolute value and rounding are configured simultaneously, the absolute value is taken first, then rounding is applied. ::: ### Get the value of a time in a specific unit - Receives input value type: Date - Parameters - Time unit. - Output value type: Number - Example: When the input value is `2024-7-15 00:00:00` and the unit is "day", the calculation result is `15`. ### Set the date to the start of a specific unit - Receives input value type: Date - Parameters - Time unit. - Output value type: Date - Example: When the input value is `2024-7-15 14:26:30` and the unit is "day", the calculation result is `2024-7-15 00:00:00`. ### Set the date to the end of a specific unit - Receives input value type: Date - Parameters - Time unit. - Output value type: Date - Example: When the input value is `2024-7-15 14:26:30` and the unit is "day", the calculation result is `2024-7-15 23:59:59`. ### Check for leap year - Receives input value type: Date - Parameters - No parameters - Output value type: Boolean - Example: When the input value is `2024-7-15 14:26:30`, the calculation result is `true`. ### Format as string - Receives input value type: Date - Parameters - Format, refer to [Day.js: Format](https://day.js.org/docs/en/display/format) - Output value type: String - Example: When the input value is `2024-7-15 14:26:30` and the format is `the time is YYYY/MM/DD HH:mm:ss`, the calculation result is `the time is 2024/07/15 14:26:30`. ### Convert unit - Receives input value type: Number - Parameters - Time unit before conversion. - Time unit after conversion. - Rounding operation, options include keeping decimals, rounding, rounding up, and rounding down. - Output value type: Number - Example: When the input value is `2`, the unit before conversion is "week", the unit after conversion is "day", and decimals are not kept, the calculation result is `14`. ## Example ![Date Calculation Node_Example](https://static-docs.nocobase.com/20240817184137.png) Suppose there is a promotional event, and we want to add a promotion end time to a product's field when each product is created. This end time is at 23:59:59 on the last day of the week following the product's creation time. So we can create two time functions and run them in a pipeline: - Calculate the time for the next week - Reset the result to 23:59:59 on the last day of that week This way, we get the desired time value and pass it to the next node, such as a collection modification node, to add the promotion end time to the collection. --- url: /workflow/nodes/delay.md --- # Delay ## Introduction The Delay node can add a delay to the workflow. After the delay ends, it can either continue to execute the subsequent nodes or terminate the workflow in advance, depending on the configuration. It is often used with the Parallel Branch node. A Delay node can be added to one of the branches to handle post-timeout processing. For example, in a parallel branch, one branch contains manual processing and the other contains a Delay node. When the manual processing times out, setting it to fail on timeout requires the manual processing to be completed within the limited time. Setting it to continue on timeout allows the manual processing to be ignored after the time is up. ## Installation Built-in plugin, no installation required. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Delay" node: ![Create Delay Node](https://static-docs.nocobase.com/d0816999c9f7acaec1c409bd8fb6cc36.png) ## Node Configuration ![Delay Node_Node Configuration](https://static-docs.nocobase.com/5fe8a36535f20a087a0148ffa1cd2aea.png) ### Delay Time For the delay time, you can enter a number and select a time unit. Supported time units are: seconds, minutes, hours, days, and weeks. ### On Timeout Status For the on timeout status, you can choose "Pass and continue" or "Fail and exit". The former means that after the delay ends, the workflow will continue to execute the subsequent nodes. The latter means that after the delay ends, the workflow will terminate prematurely with a failed status. ## Example Take the scenario where a work order needs a reply within a limited time after being initiated. We need to add a manual node in one of two parallel branches and a Delay node in the other. If the manual processing is not replied to within 10 minutes, the work order status is updated to "timed out and unprocessed". ![Delay Node_Example_Flow Organization](https://static-docs.nocobase.com/898c84adc376dc211b003a62e16e8e5b.png) --- url: /workflow/nodes/destroy.md --- # Delete data Used to delete data from a collection that meets certain conditions. The basic usage of the delete node is similar to the update node, except that the delete node does not require field assignment. You only need to select the collection and filter conditions. The result of the delete node returns the number of rows successfully deleted, which can only be viewed in the execution history and cannot be used as a variable in subsequent nodes. :::info{title=Note} Currently, the delete node does not support row-by-row deletion; it performs batch deletions. Therefore, it will not trigger other events for each individual data deletion. ::: ## Create node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Delete data" node: ![Create delete data node](https://static-docs.nocobase.com/e1d6b8728251fcdbed6c7f50e5570da2.png) ## Node configuration ![Delete node_Node configuration](https://static-docs.nocobase.com/580600c2b13ef4e01dfa48b23539648e.png) ### Collection Select the collection from which to delete data. ### Filter conditions Similar to the filter conditions for a regular collection query, you can use the workflow's context variables. ## Example For example, to periodically clean up canceled and invalid historical order data, you can use the delete node: ![Delete node_Example_Node configuration](https://static-docs.nocobase.com/b94b75077a17252f8523c3f13ce5f320.png) The workflow will be triggered periodically and execute the deletion of all canceled and invalid historical order data. --- url: /workflow/nodes/end.md --- # End Workflow When this node is executed, it immediately terminates the current workflow with the status configured in the node. It is typically used for flow control based on specific logic, exiting the current workflow when certain conditions are met and stopping the execution of subsequent processes. It is analogous to the `return` instruction in programming languages, used to exit the current function. ## Add Node In the workflow configuration interface, click the plus ("+") button in the flow to add an "End Workflow" node: ![End Workflow_Add](https://static-docs.nocobase.com/672186ab4c8f7313dd3cf9c880b524b8.png) ## Node Configuration ![End Workflow_Node Configuration](https://static-docs.nocobase.com/bb6a597f25e9afb72836a14a0fe0683e.png) ### End Status The end status will affect the final status of the workflow execution. It can be configured as "Succeeded" or "Failed". When the workflow execution reaches this node, it will exit immediately with the configured status. :::info{title=Note} When used in a "Before action event" type workflow, it will intercept the request that initiated the action. For details, please refer to [Usage of "Before action event"](../triggers/pre-action). Also, in addition to intercepting the request that initiated the action, the end status configuration will also affect the status of the feedback in the "response message" for this type of workflow. ::: --- url: /workflow/nodes/index.md --- # Overview A workflow is typically composed of several connected operational steps. Each node represents one of these steps and serves as a basic logical unit in the process. Just like in a programming language, different types of nodes represent different instructions, which determine the node's behavior. When the workflow runs, the system enters each node sequentially and executes its instructions. :::info{title=Note} A workflow's trigger is not a node. It is only displayed as an entry point in the flowchart, but it is a different concept from a node. For details, please refer to the [Triggers](../triggers/index.md) content. ::: From a functional perspective, the currently implemented nodes can be divided into several major categories (28 types of nodes in total): - Artificial Intelligence - [Large Language Model](../../ai-employees/workflow/nodes/llm/chat.md) (provided by @nocobase/plugin-workflow-llm plugin) - Control Flow - [Condition](./condition.md) - [Multi-conditions](./multi-conditions.md) - [Loop](./loop.md) (provided by @nocobase/plugin-workflow-loop plugin) - [Variable](./variable.md) (provided by @nocobase/plugin-workflow-variable plugin) - [Parallel Branch](./parallel.md) (provided by @nocobase/plugin-workflow-parallel plugin) - [Invoke Workflow](./subflow.md) (provided by @nocobase/plugin-workflow-subflow plugin) - [Workflow Output](./output.md) (provided by @nocobase/plugin-workflow-subflow plugin) - [JSON Variable Mapping](./json-variable-mapping.md) (provided by @nocobase/plugin-workflow-json-variable-mapping plugin) - [Delay](./delay.md) (provided by @nocobase/plugin-workflow-delay plugin) - [End Workflow](./end.md) - Calculation - [Calculation](./calculation.md) - [Date Calculation](./date-calculation.md) (provided by @nocobase/plugin-workflow-date-calculation plugin) - [JSON Calculation](./json-query.md) (provided by @nocobase/plugin-workflow-json-query plugin) - Collection Actions - [Create Data](./create.md) - [Update Data](./update.md) - [Delete Data](./destroy.md) - [Query Data](./query.md) - [Aggregate Query](./aggregate.md) (provided by @nocobase/plugin-workflow-aggregate plugin) - [SQL Action](./sql.md) (provided by @nocobase/plugin-workflow-sql plugin) - Manual Handling - [Manual Handling](./manual.md) (provided by @nocobase/plugin-workflow-manual plugin) - [Approval](./approval.md) (provided by @nocobase/plugin-workflow-approval plugin) - [CC](./cc.md) (provided by @nocobase/plugin-workflow-cc plugin) - Other Extensions - [HTTP Request](./request.md) (provided by @nocobase/plugin-workflow-request plugin) - [JavaScript](./javascript.md) (provided by @nocobase/plugin-workflow-javascript plugin) - [Send Email](./mailer.md) (provided by @nocobase/plugin-workflow-mailer plugin) - [Notification](../../notification-manager/index.md#工作流通知节点) (provided by @nocobase/plugin-workflow-notification plugin) - [Response](./response.md) (provided by @nocobase/plugin-workflow-webhook plugin) - [Response Message](./response-message.md) (provided by @nocobase/plugin-workflow-response-message plugin) --- url: /workflow/nodes/javascript.md --- # JavaScript Script ## Introduction The JavaScript Script node allows users to execute a custom server-side JavaScript script within a workflow. The script can use variables from upstream in the workflow as parameters, and its return value can be provided to downstream nodes. The script runs in a worker thread on the NocoBase application's server. By default, it uses a secure sandbox (isolated-vm) that does not support `require` or Node.js built-in APIs. For details, see [Execution Engine](#execution-engine) and [Feature List](#feature-list). ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "JavaScript" node: ![20241202203457](https://static-docs.nocobase.com/20241202203457.png) ## Node Configuration ![20241202203655](https://static-docs.nocobase.com/20241202203655.png) ### Parameters Used to pass variables or static values from the workflow context into the script for use in the code logic. `name` is the parameter name, which becomes the variable name once passed into the script. `value` is the parameter value, which can be a variable or a constant. ### Script Content The script content can be considered a function. You can write any JavaScript code supported in the Node.js environment and use the `return` statement to return a value as the node's execution result, which can be used as a variable by subsequent nodes. After writing the code, you can click the test button below the editor to open a test execution dialog, where you can fill in parameters with static values for a simulated run. After execution, you can see the return value and output (log) content in the dialog. ![20241202203833](https://static-docs.nocobase.com/20241202203833.png) ### Timeout Setting The unit is milliseconds. A value of `0` means no timeout is set. ### Continue on error If checked, subsequent nodes will still be executed even if the script encounters an error or times out. :::info{title="Note"} If the script errors out, it will have no return value, and the node's result will be populated with the error message. If subsequent nodes use the result variable from the script node, it should be handled with caution. ::: ## Execution Engine The JavaScript script node supports two execution engines, automatically selected based on whether the `WORKFLOW_SCRIPT_MODULES` environment variable is configured: ### Safe Mode (Default) When `WORKFLOW_SCRIPT_MODULES` is **not configured**, scripts run using the [isolated-vm](https://github.com/laverdet/isolated-vm) engine. This engine executes code in an isolated V8 environment with the following characteristics: - **Does not support** `require` — no modules can be imported - **Does not support** Node.js built-in APIs (such as `process`, `Buffer`, `global`, etc.) - Only ECMAScript standard built-in objects are available (such as `JSON`, `Math`, `Promise`, `Date`, etc.) - Supports passing data via parameters, `console` for logging, and `async`/`await` This is the recommended default mode, suitable for pure computation and data processing logic, providing the highest level of security isolation. ### Unsafe Mode (Module Support) When `WORKFLOW_SCRIPT_MODULES` **is configured**, scripts switch to the Node.js built-in `vm` engine to enable `require` capability. :::warning{title="Security Warning"} In unsafe mode, although scripts run in a `vm` sandbox with a restricted module whitelist, the Node.js `vm` module is not a secure sandbox mechanism. Enabling this mode implies trusting all users who have permission to edit workflow scripts. Administrators should assess the security risks independently and strictly control the module whitelist and workflow editing permissions. ::: Modules can be used in the script consistent with CommonJS, using the `require()` directive to import modules. Supports native Node.js modules and modules installed in `node_modules` (including dependencies already used by NocoBase). Modules to be made available to the code must be declared in the application's environment variable `WORKFLOW_SCRIPT_MODULES`, with multiple package names separated by commas, for example: ```ini WORKFLOW_SCRIPT_MODULES=crypto,timers,lodash,dayjs ``` :::info{title="Note"} Modules not declared in the `WORKFLOW_SCRIPT_MODULES` environment variable **cannot** be used in the script, even if they are native to Node.js or already installed in `node_modules`. This policy can be used at the operational level to control the list of modules available to users, preventing scripts from having excessive permissions in some scenarios. ::: When in a non-source-deployed environment, if a module is not installed in `node_modules`, you can manually install the required package into the `storage` directory. For example, to use the `exceljs` package, you can perform the following steps: ```shell cd storage npm i --no-save --no-package-lock --prefix . exceljs ``` Then add the package's relative (or absolute) path based on the application's CWD (current working directory) to the environment variable `WORKFLOW_SCRIPT_MODULES`: ```ini WORKFLOW_SCRIPT_MODULES=./storage/node_modules/exceljs ``` You can then use the `exceljs` package in your script (as the name used in `require` must match exactly with that defined in the environment variable): ```js const ExcelJS = require('./storage/node_modules/exceljs'); // ... ``` ## Feature List ### Node.js Version Same as the Node.js version running the main application. ### Global Variables **Does not support** global variables such as `global`, `process`, `__dirname`, and `__filename`. ```js console.log(global); // will throw error: "global is not defined" ``` ### Input Parameters Parameters configured in the node become global variables within the script and can be used directly. Parameters passed to the script only support basic types, such as `boolean`, `number`, `string`, `object`, and arrays. A `Date` object will be converted to an ISO format string when passed in. Other complex types, such as instances of custom classes, cannot be passed directly. ### Return Value The `return` statement can be used to return basic data types (same rules as parameters) to the node as its result. If the `return` statement is not called in the code, the node execution will have no return value. ```js return 123; ``` ### Output (Log) **Supports** using `console` to output logs. ```js console.log('hello world!'); ``` When the workflow is executed, the output of the script node is also recorded in the corresponding workflow's log file. ### Asynchronous **Supports** using `async` to define asynchronous functions and `await` to call them. **Supports** using the `Promise` global object. ```js async function test() { return Promise.resolve(1); } const value = await test(); return value; ``` ### Timers To use methods like `setTimeout`, `setInterval`, or `setImmediate`, you need to import them from the Node.js `timers` package (only available in unsafe mode). ```js const { setTimeout, setInterval, setImmediate, clearTimeout, clearInterval, clearImmediate } = require('timers'); async function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); } await sleep(1000); return 123; ``` --- url: /workflow/nodes/json-query.md --- # JSON Calculation ## Introduction Based on different JSON calculation engines, it calculates or transforms complex JSON data generated by upstream nodes for use by subsequent nodes. For example, the results of SQL operation and HTTP request nodes can be transformed into the required values and variable formats through this node for use by subsequent nodes. ## Create Node In the workflow configuration interface, click the plus ("+") button in the process to add a "JSON Calculation" node: ![Create Node](https://static-docs.nocobase.com/7de796517539ad9dfc88b7160f1d0dd7.png) :::info{title=Note} Usually, the JSON Calculation node is created below other data nodes to parse them. ::: ## Node Configuration ### Parsing Engine The JSON Calculation node supports different syntaxes through different parsing engines. You can choose based on your preferences and the features of each engine. Currently, three parsing engines are supported: - [JMESPath](https://jmespath.org/) - [JSONPath Plus](https://jsonpath-plus.github.io/JSONPath/docs/ts/) - [JSONata](https://jsonata.org/) ![Engine Selection](https://static-docs.nocobase.com/29be3b92a62b7d20312d1673e749f2ec.png) ### Data Source The data source can be the result of an upstream node or a data object in the workflow context. It is usually a data object without a built-in structure, such as the result of an SQL node or an HTTP request node. ![Data Source](https://static-docs.nocobase.com/f5a97e20693b3d30b3a994a576aa282d.png) :::info{title=Note} Usually, the data objects of collection-related nodes are structured through collection configuration information and generally do not need to be parsed by the JSON Calculation node. ::: ### Parsing Expression Custom parsing expressions based on parsing requirements and the chosen parsing engine. ![Parsing Expression](https://static-docs.nocobase.com/181abd162fd32c09b62f6aa1d1cb3ed4.png) :::info{title=Note} Different engines provide different parsing syntaxes. For details, please refer to the documentation in the links. ::: Since `v1.0.0-alpha.15`, expressions support variables. Variables are pre-parsed before the specific engine executes, replacing the variables with specific string values according to string template rules, and concatenating them with other static strings in the expression to form the final expression. This feature is very useful when you need to dynamically build expressions, for example, when some JSON content needs a dynamic key for parsing. ### Property Mapping When the calculation result is an object (or an array of objects), you can further map the required properties to child variables through property mapping for use by subsequent nodes. ![Property Mapping](https://static-docs.nocobase.com/b876abe4ccf6b4709eb8748f21ef3527.png) :::info{title=Note} For an object (or array of objects) result, if property mapping is not performed, the entire object (or array of objects) will be saved as a single variable in the node's result, and the property values of the object cannot be used directly as variables. ::: ## Example Suppose the data to be parsed is from a preceding SQL node used to query data, and its result is a set of order data: ```json [ { "id": 1, "products": [ { "id": 1, "title": "Product 1", "price": 100, "quantity": 1 }, { "id": 2, "title": "Product 2", "price": 120, "quantity": 2 } ] }, { "id": 2, "products": [ { "id": 3, "title": "Product 3", "price": 130, "quantity": 1 }, { "id": 4, "title": "Product 4", "price": 140, "quantity": 2 } ] } ] ``` If we need to parse and calculate the total price of the two orders in the data, and assemble it with the corresponding order ID into an object to update the order's total price, we can configure it as follows: ![Example - Parse SQL Configuration](https://static-docs.nocobase.com/e62322a868b26ff98120bfcd6dcdb3bd.png) 1. Select the JSONata parsing engine; 2. Select the result of the SQL node as the data source; 3. Use the JSONata expression `$[0].{"id": id, "total": products.(price * quantity)}` to parse; 4. Select property mapping to map `id` and `total` to child variables; The final parsing result is as follows: ```json [ { "id": 1, "total": 340 }, { "id": 2, "total": 410 } ] ``` Then, loop through the resulting order array to update the total price of the orders. ![Update the total price of the corresponding order](https://static-docs.nocobase.com/b3329b0efe4471f5eed1f0673bef740e.png) --- url: /workflow/nodes/json-variable-mapping.md --- # JSON Variable Mapping > v1.6.0 ## Introduction Used to map complex JSON structures from the results of upstream nodes into variables for use in subsequent nodes. For example, after mapping the results of SQL Action and HTTP Request nodes, their property values can be used in subsequent nodes. :::info{title=Tip} Unlike the JSON Calculation node, the JSON Variable Mapping node does not support custom expressions and is not based on a third-party engine. It is only used to map property values in a JSON structure, but it is simpler to use. ::: ## Create Node In the workflow configuration interface, click the plus (+) button in the flow to add a "JSON Variable Mapping" node: ![Create Node](https://static-docs.nocobase.com/20250113173635.png) ## Node Configuration ### Data Source The data source can be the result of an upstream node or a data object in the process context. It is usually an unstructured data object, such as the result of an SQL node or an HTTP Request node. ![Data Source](https://static-docs.nocobase.com/20250113173720.png) ### Input Sample Data Paste sample data and click the parse button to automatically generate a list of variables: ![Input Sample Data](https://static-docs.nocobase.com/20250113182327.png) If there are any variables in the automatically generated list that you don't need, you can click the delete button to remove them. :::info{title=Tip} The sample data is not the final execution result; it is only used to assist in generating the variable list. ::: ### Path Includes Array Index If not checked, the array content will be mapped according to the default variable handling method of NocoBase workflows. For example, input the following sample: ```json { "a": 1, "b": [ { "c": 2 }, { "c": 3 } ] } ``` In the generated variables, `b.c` will represent the array `[2, 3]`. If this option is checked, the variable path will include the array index, for example, `b.0.c` and `b.1.c`. ![20250113184056](https://static-docs.nocobase.com/20250113184056.png) When including array indices, you need to ensure that the array indices in the input data are consistent; otherwise, it will cause a parsing error. ## Use in Subsequent Nodes In the configuration of subsequent nodes, you can use the variables generated by the JSON Variable Mapping node: ![20250113203658](https://static-docs.nocobase.com/20250113203658.png) Although the JSON structure can be complex, after mapping, you only need to select the variable for the corresponding path. --- url: /workflow/nodes/loop.md --- # Loop ## Introduction A loop is equivalent to syntax structures like `for`/`while`/`forEach` in programming languages. When you need to repeat some operations a certain number of times or for a data collection (array), you can use a loop node. ## Installation This is a built-in plugin and does not require installation. ## Creating a Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Loop" node: ![Creating a Loop Node](https://static-docs.nocobase.com/b3c8061a66bfff037f4b9509ab0aad75.png) After creating a loop node, a branch inside the loop will be generated. You can add any number of nodes within this branch. These nodes can use not only the variables from the workflow context but also local variables from the loop context, such as the data object being iterated over in the loop collection, or the index of the loop count (the index starts from `0`). The scope of local variables is limited to within the loop. If there are nested loops, you can use the local variables of the specific loop at each level. ## Node Configuration ![20241016135326](https://static-docs.nocobase.com/20241016135326.png) ### Loop Object The loop handles different data types of the loop object differently: 1. **Array**: This is the most common case. You can usually select a variable from the workflow context, such as the multiple data results from a query node, or preloaded one-to-many relationship data. If an array is selected, the loop node will iterate through each element in the array, assigning the current element to a local variable in the loop context for each iteration. 2. **Number**: When the selected variable is a number, it will be used as the number of iterations. The value must be a positive integer; negative numbers will not enter the loop, and the decimal part of a number will be ignored. The index of the loop count in the local variable is also the value of the loop object. This value starts from **0**. For example, if the loop object number is 5, the object and index in each loop will be: 0, 1, 2, 3, 4. 3. **String**: When the selected variable is a string, its length will be used as the number of iterations, processing each character of the string by index. 4. **Other**: Other types of values (including object types) are treated as a single-item loop object and will only loop once. This situation usually does not require a loop. Besides selecting a variable, you can also directly input constants for number and string types. For example, inputting `5` (number type) will cause the loop node to iterate 5 times. Inputting `abc` (string type) will cause the loop node to iterate 3 times, processing the characters `a`, `b`, and `c` respectively. In the variable selection tool, choose the desired type for the constant. ### Loop Condition Since version `v1.4.0-beta`, options related to loop conditions have been added. You can enable loop conditions in the node configuration. **Condition** Similar to the condition configuration in a condition node, you can combine configurations and use variables from the current loop, such as the loop object, loop index, etc. **Check Timing** Similar to the `while` and `do/while` constructs in programming languages, you can choose to evaluate the configured condition before each loop starts or after each loop ends. Post-condition evaluation allows the other nodes within the loop body to execute for one round before the condition is checked. **When Condition is Not Met** Similar to the `break` and `continue` statements in programming languages, you can choose to exit the loop or continue to the next iteration. ### Handling Errors in Loop Nodes Since version `v1.4.0-beta`, when a node inside the loop fails to execute (due to unmet conditions, errors, etc.), you can configure the subsequent flow. Three handling methods are supported: * Exit workflow (like `throw` in programming) * Exit loop and continue workflow (like `break` in programming) * Continue to the next loop object (like `continue` in programming) The default is "Exit workflow," which can be changed as needed. ## Environment Variables ### WORKFLOW_LOOP_LIMIT Used to limit the maximum number of iterations for loop nodes by operations roles, preventing infinite loops caused by configuration errors. The default is unlimited, and this limit can be adjusted by configuring this environment variable. ```ini # Limit to a maximum of 100 iterations WORKFLOW_LOOP_LIMIT=100 ``` If a limit value is set, when the loop node executes and exceeds this number, it will directly report an error and exit the process. The node result will include the `exceeded` information with a value of true. ## Example For example, when an order is placed, you need to check the stock for each product in the order. If the stock is sufficient, deduct the stock; otherwise, update the product in the order detail as invalid. 1. Create three collections: Products <-(1:m)-- Order Details --(m:1)-> Orders. The data model is as follows: **Orders Collection** | Field Name | Field Type | | ------------ | -------------- | | Order Details | One-to-Many (Order Details) | | Order Total Price | Number | **Order Details Collection** | Field Name | Field Type | | -------- | -------------- | | Product | Many-to-One (Product) | | Quantity | Number | **Products Collection** | Field Name | Field Type | | -------- | -------- | | Product Name | Single Line Text | | Price | Number | | Stock | Integer | 2. Create a workflow. For the trigger, select "Collection Event," and choose the "Orders" collection to trigger "After record added." You also need to configure it to preload the relationship data of the "Order Details" collection and the Products collection under the details: ![Loop Node_Example_Trigger Configuration](https://static-docs.nocobase.com/0086601c2fc0e17a64d046a4c86b49b7.png) 3. Create a loop node and select the loop object as "Trigger data / Order Details," which means it will process each record in the Order Details collection: ![Loop Node_Example_Loop Node Configuration](https://static-docs.nocobase.com/2507becc32db5a9a0641c198605a20da.png) 4. Inside the loop node, create a "Condition" node to check if the product's stock is sufficient: ![Loop Node_Example_Condition Node Configuration](https://static-docs.nocobase.com/a6d08d15786841e1a3512b38e4629852.png) 5. If it is sufficient, create a "Calculation node" and an "Update record" node in the "Yes" branch to update the corresponding product record with the calculated deducted stock: ![Loop Node_Example_Calculation Node Configuration](https://static-docs.nocobase.com/8df3604c71f8f8705b1552d3ebfe3b50.png) ![Loop Node_Example_Update Stock Node Configuration](https://static-docs.nocobase.com/2d84baa9b3b01bd85fccda9eec992378.png) 6. Otherwise, in the "No" branch, create an "Update record" node to update the status of the order detail to "invalid": ![Loop Node_Example_Update Order Detail Node Configuration](https://static-docs.nocobase.com/4996613090c254c69a1d80f3b3a7fae2.png) The overall workflow structure is as follows: ![Loop Node_Example_Workflow Structure](https://static-docs.nocobase.com/6f59ef246c1f19976344a7624c4c4151.png) After configuring and activating this workflow, when a new order is created, it will automatically check the stock of the products in the order details. If the stock is sufficient, it will be deducted; otherwise, the product in the order detail will be updated to invalid (so that a valid order total can be calculated). --- url: /workflow/nodes/mailer.md --- # Send email ## Introduction Used to send emails. Supports content in text and HTML formats. ## Create node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Send email" node: ![20251031130522](https://static-docs.nocobase.com/20251031130522.png) ## Node configuration ![20251031131125](https://static-docs.nocobase.com/20251031131125.png) Each option can use variables from the workflow context. For sensitive information, global variables and secrets can also be used. ## FAQ ### Gmail sending frequency limit When sending some emails, you may encounter the following error: ```json { "code": "ECONNECTION", "response": "421-4.7.0 Try again later, closing connection. (EHLO)\n421-4.7.0 For more information, go to\n421 4.7.0 About SMTP error messages - Google Workspace Admin Help 3f1490d57ef6-e7f7352f44csm831688276.30 - gsmtp", "responseCode": 421, "command": "EHLO" } ``` This is because Gmail rate-limits sending requests from domains that are not specified. When deploying the application, you need to configure the server's hostname to the domain you have configured in Gmail. For example, in a Docker deployment: ```yaml services: app: image: nocobase/nocobase hostname: # Set to your configured sending domain ``` Reference: [Google SMTP Relay - Intermittent problems](https://forum.nocobase.com/t/google-smtp-relay-intermittent-problems/5483/6) --- url: /workflow/nodes/manual.md --- # Manual ## Introduction When a business process cannot be fully automated for decision-making, a manual node can be used to delegate some decision-making authority to a person. When a manual node is executed, it interrupts the entire workflow execution and generates a to-do task for the corresponding user. After the user submits the task, the workflow will either continue, remain pending, or be terminated based on the selected status. This is very useful in scenarios like approval processes. ## Installation Built-in plugin, no installation required. ## Create Node In the workflow configuration interface, click the plus ("+") button in the workflow to add a "Manual" node: ![Create Manual Node](https://static-docs.nocobase.com/4dd259f1aceeaf9b825abb4b257df909.png) ## Configure Node ### Assignee A manual node needs to specify a user as the executor of the to-do task. The list of to-do tasks can be added as a block on a page, and the content of the task popup for each node needs to be configured in the node's interface configuration. Select a user, or select the primary key or foreign key of user data from the context via a variable. ![Manual Node_Configure_Assignee_Select Variable](https://static-docs.nocobase.com/22fbca3b8e21fda3a831019037001445.png) :::info{title=Note} Currently, the assignee option for manual nodes does not support multiple users. This will be supported in a future version. ::: ### Configure User Interface The interface configuration for the to-do item is the core part of the manual node. You can click the "Configure user interface" button to open a separate configuration popup, which can be configured in a WYSIWYG manner, just like a regular page: ![Manual Node_Node Configuration_Interface Configuration](https://static-docs.nocobase.com/fd360168c879743cf22d57440cd2590f.png) #### Tabs Tabs can be used to distinguish different content. For example, one tab for an approval form submission, another for a rejection form submission, or for displaying details of related data. They can be freely configured. #### Blocks The supported block types are mainly divided into two categories: data blocks and form blocks. Additionally, Markdown is primarily used for static content such as informational messages. ##### Data Block Data blocks can display trigger data or the processing results of any node, providing relevant contextual information to the to-do assignee. For example, if the workflow is triggered by a form event, you can create a details block for the trigger data. This is consistent with the details configuration of a regular page, allowing you to select any field from the trigger data for display: ![Manual Node_Node Configuration_Interface Configuration_Data Block_Trigger](https://static-docs.nocobase.com/675c3e58a1a4f45db310a72c2d0a404c.png) Node data blocks are similar; you can select the data result from an upstream node to display as details. For example, the result of an upstream calculation node can serve as contextual reference information for the assignee's to-do task: ![Manual Node_Node Configuration_Interface Configuration_Data Block_Node Data](https://static-docs.nocobase.com/a583e26e508e954b47e5ddff80d998c4.png) :::info{title=Note} Since the workflow is not in an executed state during interface configuration, no specific data is displayed in the data blocks. The relevant data for a specific workflow instance can only be seen in the to-do popup interface after the workflow has been triggered and executed. ::: ##### Form Block At least one form block must be configured in the to-do interface to handle the final decision on whether the workflow should continue. Not configuring a form will prevent the workflow from proceeding after it is interrupted. There are three types of form blocks: - Custom form - Create record form - Update record form ![Manual Node_Node Configuration_Interface Configuration_Form Types](https://static-docs.nocobase.com/2d068f3012ab07e32a265405492104a8.png) Create record forms and update record forms require selecting a base collection. After the to-do user submits, the values in the form will be used to create or update data in the specified collection. A custom form allows you to freely define a temporary form that is not tied to a collection. The field values submitted by the to-do user can be used in subsequent nodes. The form's submit buttons can be configured into three types: - Submit and continue workflow - Submit and terminate workflow - Only save form values ![Manual Node_Node Configuration_Interface Configuration_Form Buttons](https://static-docs.nocobase.com/6b45995b14152e85a821dff6f6e3189a.png) The three buttons represent three node statuses in the workflow process. After submission, the node's status changes to "Completed," "Rejected," or remains in a "Pending" state. A form must have at least one of the first two configured to determine the subsequent flow of the entire workflow. On the "Continue workflow" button, you can configure assignments for form fields: ![Manual Node_Node Configuration_Interface Configuration_Form Button_Set Form Values](https://static-docs.nocobase.com/2cec2d4e2957f068877e616dec3b56dd.png) ![Manual Node_Node Configuration_Interface Configuration_Form Button_Set Form Values Popup](https://static-docs.nocobase.com/5ff51b60c76cdb76e6f1cc95dc3d8640.png) After opening the popup, you can assign values to any form field. After the form is submitted, this value will be the final value of the field. This is particularly useful when reviewing data. You can use multiple different "Continue workflow" buttons in a form, with each button setting different enum values for fields like status, thus achieving the effect of continuing the subsequent workflow execution with different data values. ## To-do Block For manual processing, you also need to add a to-do list to a page to display to-do tasks. This allows the relevant personnel to access and handle the specific tasks of the manual node through this list. ### Add Block You can select "Workflow To-do" from the blocks on a page to add a to-do list block: ![Manual Node_Add To-do Block](https://static-docs.nocobase.com/198b417454cd73b704267bf30fe5e647.png) To-do list block example: ![Manual Node_To-do List](https://static-docs.nocobase.com/cfefb0d2c6a91c5c9dfa550d6b220f34.png) ### To-do Details Afterward, the relevant personnel can click on the corresponding to-do task to open the to-do popup and perform the manual processing: ![Manual Node_To-do Details](https://static-docs.nocobase.com/ccfd0533deebff6b3f6ef4408066e688.png) ## Example ### Post Review Suppose a post submitted by a regular user needs to be approved by an administrator before it can be updated to a published state. If the workflow is rejected, the post will remain in a draft state (not public). This process can be implemented using an update form in a manual node. Create a workflow triggered by "Create Post" and add a manual node:
Manual Node_Example_Post Review_Workflow Orchestration
In the manual node, configure the assignee as an administrator. In the interface configuration, add a block based on the trigger data to display the details of the new post:
Manual Node_Example_Post Review_Node Configuration_Details Block
In the interface configuration, add a block based on an update record form, select the posts collection, for the administrator to decide whether to approve. After approval, the corresponding post will be updated based on other subsequent configurations. After adding the form, there will be a "Continue workflow" button by default, which can be considered as "Approve". Then, add a "Terminate workflow" button to be used for rejection:
Manual Node_Example_Post Review_Node Configuration_Form and Actions
When continuing the workflow, we need to update the post's status. There are two ways to configure this. One is to display the post's status field directly in the form for the operator to select. This method is more suitable for situations that require active form filling, such as providing feedback:
Manual Node_Example_Post Review_Node Configuration_Form Fields
To simplify the operator's task, another way is to configure form value assignment on the "Continue workflow" button. In the assignment, add a "Status" field with the value "Published". This means that when the operator clicks the button, the post will be updated to the published state:
Manual Node_Example_Post Review_Node Configuration_Form Assignment
Then, from the configuration menu in the upper right corner of the form block, select the filter condition for the data to be updated. Here, select the "Posts" collection, and the filter condition is "ID `equals` Trigger variable / Trigger data / ID":
Manual Node_Example_Post Review_Node Configuration_Form Condition
Finally, you can modify the titles of each block, the text of the relevant buttons, and the placeholder text of the form fields to make the interface more user-friendly:
Manual Node_Example_Post Review_Node Configuration_Final Form
Close the configuration panel and click the submit button to save the node configuration. The workflow is now configured. After enabling this workflow, it will be automatically triggered when a new post is created. The administrator can see that this workflow needs to be processed from the to-do task list. Clicking to view will show the details of the to-do task:
Manual Node_Example_Post Review_To-do List
Manual Node_Example_Post Review_To-do Details
The administrator can make a manual judgment based on the post details to decide whether the post can be published. If so, clicking the "Approve" button will update the post to the published state. If not, clicking the "Reject" button will keep the post in a draft state. ## Leave Approval Suppose an employee needs to request leave, which must be approved by a supervisor to take effect, and the corresponding employee's leave data needs to be deducted. Regardless of approval or rejection, an HTTP request node will be used to call an SMS API to send a notification message to the employee (see the [HTTP Request](#_HTTP_请求) section). This scenario can be implemented using a custom form in a manual node. Create a workflow triggered by "Create Leave Request" and add a manual node. This is similar to the previous post review process, but here the assignee is the supervisor. In the interface configuration, add a block based on the trigger data to display the details of the new leave request. Then, add another block based on a custom form for the supervisor to decide whether to approve. In the custom form, add a field for approval status and a field for the reason for rejection:
Manual Node_Example_Leave Approval_Node Configuration
Unlike the post review process, since we need to continue the subsequent process based on the supervisor's approval result, we only configure a "Continue workflow" button for submission, without using a "Terminate workflow" button. At the same time, after the manual node, we can use a conditional node to determine whether the supervisor has approved the leave request. In the approval branch, add data processing to deduct leave, and after the branches merge, add a request node to send an SMS notification to the employee. This results in the following complete workflow:
Manual Node_Example_Leave Approval_Workflow Orchestration
The condition in the conditional node is configured as "Manual node / Custom form data / Value of the approval field is 'Approved'":
Manual Node_Example_Leave Approval_Condition
The data in the send request node can also use the corresponding form variables from the manual node to differentiate the SMS content for approval and rejection. This completes the entire workflow configuration. After the workflow is enabled, when an employee submits a leave request form, the supervisor can process the approval in their to-do tasks. The operation is basically similar to the post review process. --- url: /workflow/nodes/multi-conditions.md --- # Multi-conditions v2.0.0+ ## Introduction Similar to `switch / case` or `if / else if` statements in programming languages. The system evaluates configured conditions sequentially. Once a condition is met, the workflow executes the corresponding branch and skips subsequent condition checks. If no conditions are met, the "Otherwise" branch is executed. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Multi-conditions" node: ![Create Multi-conditions Node](https://static-docs.nocobase.com/20251123222134.png) ## Branch Management ### Default Branches After creation, the node includes two branches by default: 1. **Condition Branch**: For configuring specific judgment conditions. 2. **Otherwise Branch**: Entered when no condition branches are met; requires no condition configuration. Click the "Add branch" button below the node to add more condition branches. ![20251123222540](https://static-docs.nocobase.com/20251123222540.png) ### Add Branch After clicking "Add branch", the new branch is appended before the "Else" branch. ![20251123222805](https://static-docs.nocobase.com/20251123222805.png) ### Delete Branch When multiple condition branches exist, click the trash icon on the right of a branch to delete it. If only one condition branch remains, it cannot be deleted. ![20251123223127](https://static-docs.nocobase.com/20251123223127.png) :::info{title=Note} Deleting a branch will also delete all nodes within it; please proceed with caution. The "Else" branch is a built-in branch and cannot be deleted. ::: ## Node Configuration ### Condition Configuration Click the condition name at the top of a branch to edit specific condition details: ![20251123223352](https://static-docs.nocobase.com/20251123223352.png) #### Condition Label Supports custom labels. Once filled, it will be displayed as the condition name in the flowchart. If not configured (or left empty), it defaults to "Condition 1", "Condition 2", etc., in sequence. ![20251123224209](https://static-docs.nocobase.com/20251123224209.png) #### Calculation Engine Currently supports three engines: - **Basic**: Uses simple logical comparisons (e.g., equals, contains) and "AND"/"OR" combinations to determine results. - **Math.js**: Supports expression calculation using [Math.js](https://mathjs.org/) syntax. - **Formula.js**: Supports expression calculation using [Formula.js](https://formulajs.info/) syntax (similar to Excel formulas). All three modes support using workflow context variables as parameters. ### When No Conditions Are Met In the node configuration panel, you can set the subsequent action when no conditions are met: ![20251123224348](https://static-docs.nocobase.com/20251123224348.png) * **End workflow with failure (Default)**: Marks the workflow status as failed and terminates the process. * **Continue to execute subsequent nodes**: After the current node finishes, continues executing subsequent nodes in the workflow. :::info{title=Note} Regardless of the chosen handling method, when no conditions are met, the flow will first enter the "Else" branch to execute nodes within it. ::: ## Execution History In the workflow execution history, the Multi-conditions node identifies the result of each condition using different colors: - **Green**: Condition met; entered this branch. - **Red**: Condition not met (or calculation error); skipped this branch. - **Blue**: Judgment not executed (skipped because a preceding condition was already met). ![20251123225455](https://static-docs.nocobase.com/20251123225455.png) If a configuration error causes a calculation exception, in addition to displaying as red, hovering over the condition name will show specific error information: ![20251123231014](https://static-docs.nocobase.com/20251123231014.png) When a condition calculation exception occurs, the Multi-conditions node will end with an "Error" status and will not continue executing subsequent nodes. --- url: /workflow/nodes/output.md --- # Workflow Output ## Introduction The "Workflow Output" node is used in a called workflow to define its output value. When one workflow is called by another, the "Workflow Output" node can be used to pass a value back to the caller. ## Create Node In the called workflow, add a "Workflow Output" node: ![20241231002033](https://static-docs.nocobase.com/20241231002033.png) ## Configure Node ### Output Value Enter or select a variable as the output value. The output value can be of any type, such as a constant (string, number, boolean, date, or custom JSON), or another variable from the workflow. ![20241231003059](https://static-docs.nocobase.com/20241231003059.png) :::info{title=Tip} If multiple "Workflow Output" nodes are added to a called workflow, the value of the last executed "Workflow Output" node will be output when the workflow is called. ::: --- url: /workflow/nodes/parallel.md --- # Parallel Branch The parallel branch node can divide a workflow into multiple branches. Each branch can be configured with different nodes, and the execution method varies depending on the branch mode. Use the parallel branch node in scenarios where multiple actions need to be executed simultaneously. ## Installation Built-in plugin, no installation required. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Parallel Branch" node: ![Add Parallel Branch](https://static-docs.nocobase.com/9e0f3faa0b9335270647a30477559eac.png) After adding a parallel branch node to the workflow, two sub-branches are added by default. You can also add more branches by clicking the add branch button. Any number of nodes can be added to each branch. Unnecessary branches can be removed by clicking the delete button at the start of the branch. ![Manage Parallel Branches](https://static-docs.nocobase.com/36088a8b7970c8a1771eb3ee9bc2a757.png) ## Node Configuration ### Branch Mode The parallel branch node has the following three modes: - **All succeed**: The workflow will only continue to execute the nodes after the branches finish if all branches execute successfully. Otherwise, if any branch terminates prematurely, whether due to failure, error, or any other non-successful state, the entire parallel branch node will terminate prematurely with that status. This is also known as "All mode". - **Any succeeds**: The workflow will continue to execute the nodes after the branches finish as soon as any branch executes successfully. The entire parallel branch node will only terminate prematurely if all branches terminate prematurely, whether due to failure, error, or any other non-successful state. This is also known as "Any mode". - **Any succeeds or fails**: The workflow will continue to execute the nodes after the branches finish as soon as any branch executes successfully. However, if any node fails, the entire parallel branch will terminate prematurely with that status. This is also known as "Race mode". Regardless of the mode, each branch will be executed in order from left to right until the conditions of the preset branch mode are met, at which point it will either continue to the subsequent nodes or exit prematurely. ## Example Refer to the example in [Delay Node](./delay.md). --- url: /workflow/nodes/query.md --- # Query Data Used to query and retrieve data records from a collection that meet specific conditions. You can configure it to query a single record or multiple records. The query result can be used as a variable in subsequent nodes. When querying multiple records, the result is an array. When the query result is empty, you can choose whether to continue executing subsequent nodes. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Query Data" node: ![Add Query Data Node](https://static-docs.nocobase.com/c1ef2b851b437806faf7a39c6ab9d33a.png) ## Node Configuration ![Query Node Configuration](https://static-docs.nocobase.com/20240520131324.png) ### Collection Select the collection from which to query data. ### Result Type The result type is divided into "Single Record" and "Multiple Records": - Single Record: The result is an object, which is only the first matching record, or `null`. - Multiple Records: The result will be an array containing records that match the conditions. If no records match, it will be an empty array. You can process them one by one using a Loop node. ### Filter Conditions Similar to the filter conditions in a regular collection query, you can use the workflow's context variables. ### Sort When querying one or more records, you can use sorting rules to control the desired result. For example, to query the latest record, you can sort by the "Creation Time" field in descending order. ### Pagination When the result set might be very large, you can use pagination to control the number of query results. For example, to query the latest 10 records, you can sort by the "Creation Time" field in descending order, and then set the pagination to 1 page with 10 records. ### Handling Empty Results In single record mode, if no data meets the conditions, the query result will be `null`. In multiple records mode, it will be an empty array (`[]`). You can choose whether to check "Exit workflow when query result is empty". If checked, and the query result is empty, subsequent nodes will not be executed, and the workflow will exit early with a failed status. --- url: /workflow/nodes/request.md --- # HTTP Request ## Introduction When you need to interact with another web system, you can use the HTTP Request node. When executed, this node sends an HTTP request to the specified address according to its configuration. It can carry data in JSON or `application/x-www-form-urlencoded` format to interact with external systems. If you are familiar with request sending tools like Postman, you can quickly master the usage of the HTTP Request node. Unlike these tools, all parameters in the HTTP Request node can use context variables from the current workflow, allowing for organic integration with the system's business processes. ## Installation Built-in plugin, no installation required. ## Creating a Node In the workflow configuration interface, click the plus ("+") button in the flow to add an "HTTP Request" node: ![HTTP Request_Add](https://static-docs.nocobase.com/46f2a6fc3f6869c80f8fbd362a54e644.png) ## Node Configuration ![HTTP Request Node_Configuration](https://static-docs.nocobase.com/2fcb29af66b892fa704add52e2974a52.png) ### Request Method Optional HTTP request methods: `GET`, `POST`, `PUT`, `PATCH`, and `DELETE`. ### Request URL The URL of the HTTP service, which must include the protocol part (`http://` or `https://`). Using `https://` is recommended. ### Request Data Format This is the `Content-Type` in the request header. For supported formats, see the "[Request Body](#request-body)" section. ### Request Header Configuration Key-value pairs for the request Header section. The values can use variables from the workflow context. :::info{title=Tip} The `Content-Type` request header is configured via the request data format. There is no need to fill it in here, and any override will be ineffective. ::: ### Request Parameters Key-value pairs for the request query section. The values can use variables from the workflow context. ### Request Body The Body part of the request. Different formats are supported depending on the selected `Content-Type`. #### `application/json` Supports standard JSON formatted text. You can insert variables from the workflow context using the variable button in the upper right corner of the text editor. :::info{title=Tip} Variables must be used within a JSON string, for example: `{ "a": "{{$context.data.a}}" }`. ::: #### `application/x-www-form-urlencoded` Key-value pair format. The values can use variables from the workflow context. When variables are included, they will be parsed as a string template and concatenated into the final string value. #### `application/xml` Supports standard XML formatted text. You can insert variables from the workflow context using the variable button in the upper right corner of the text editor. #### `multipart/form-data` v1.8.0+ Supports key-value pairs for form data. Files can be uploaded when the data type is set to a file object. Files can only be selected via variables from existing file objects in the context, such as the results of a query on a file collection or related data from an associated file collection. :::info{title=Tip} When selecting file data, ensure that the variable corresponds to a single file object, not a list of files (in a one-to-many or many-to-many relationship query, the value of the relationship field will be an array). ::: ### Timeout Settings When a request does not respond for a long time, the timeout setting can be used to cancel its execution. If the request times out, the current workflow will be terminated prematurely with a failed status. ### Ignore Failures The request node considers standard HTTP status codes between `200` and `299` (inclusive) as successful, and all others as failed. If the "Ignore failed requests and continue workflow" option is checked, the subsequent nodes in the workflow will continue to execute even if the request fails. ## Using the Response Result The response result of an HTTP request can be parsed by the [JSON Query](./json-query.md) node for use in subsequent nodes. Since version `v1.0.0-alpha.16`, three parts of the request node's response result can be used as separate variables: * Response status code * Response headers * Response data ![HTTP Request Node_Using Response Result](https://static-docs.nocobase.com/20240529110610.png) The response status code is usually a standard HTTP status code in numeric form, such as `200`, `403`, etc. (as provided by the service provider). The response headers are in JSON format. Both the headers and the JSON-formatted response data still need to be parsed using a JSON node before they can be used. ## Example For example, we can use the request node to connect with a cloud platform to send notification SMS. The configuration for a cloud SMS API might look like the following (you will need to consult the specific API's documentation to adapt the parameters): ![HTTP Request Node_Configuration](https://static-docs.nocobase.com/20240515124004.png) When the workflow triggers this node, it will call the SMS API with the configured content. If the request is successful, an SMS will be sent through the cloud SMS service. --- url: /workflow/nodes/response-message.md --- # Response Message ## Introduction The response message node is used to send custom messages from the workflow back to the client that submitted the action in specific types of workflows. :::info{title=Note} Currently, it is supported for use in "Before action event" and "Custom action event" type workflows in synchronous mode. ::: ## Creating a Node In supported workflow types, you can add a "Response message" node anywhere in the workflow. Click the plus ("+") button in the workflow to add a "Response message" node: ![Adding a node](https://static-docs.nocobase.com/eac2b3565e95e4ce59f340624062ed3d.png) The response message exists as an array throughout the request process. Whenever a response message node is executed in the workflow, the new message content is appended to the array. When the server sends the response, all messages are sent to the client together. ## Node Configuration The message content is a template string where variables can be inserted. You can organize this template content in the node configuration: ![Node configuration](https://static-docs.nocobase.com/d5fa5f4002d50baf3ba16048818fddfc.png) When the workflow executes to this node, the template will be parsed to generate the message content. In the configuration above, the variable "Local variable / Loop all products / Loop object / Product / Title" will be replaced with a specific value in the actual workflow, for example: ``` Product "iPhone 14 pro" is out of stock ``` ![Message content](https://static-docs.nocobase.com/06bd4a6b6ec499c853f0c39987f63a6a.png) ## Workflow Configuration The status of the response message depends on the success or failure of the workflow execution. The failure of any node will cause the entire workflow to fail. In this case, the message content will be returned to the client with a failure status and displayed. If you need to actively define a failure state in the workflow, you can use an "End node" and configure it to a failure state. When this node is executed, the workflow will exit with a failure status, and the message will be returned to the client with a failure status. If the entire workflow does not produce a failure state and executes successfully to the end, the message content will be returned to the client with a success status. :::info{title=Note} If multiple response message nodes are defined in the workflow, the executed nodes will append the message content to an array. When finally returning to the client, all message content will be returned and displayed together. ::: ## Use Cases ### "Before action event" Workflow Using a response message in a "Before action event" workflow allows sending corresponding message feedback to the client after the workflow ends. For details, refer to [Before action event](../triggers/pre-action.md). ### "Custom action event" Workflow Using a response message in a "Custom action event" in synchronous mode allows sending corresponding message feedback to the client after the workflow ends. For details, refer to [Custom action event](../triggers/custom-action.md). --- url: /workflow/nodes/response.md --- # HTTP Response ## Introduction This node is only supported in synchronous Webhook workflows and is used to return a response to a third-party system. For example, during the processing of a payment callback, if the business process has an unexpected result (such as an error or failure), you can use the response node to return an error response to the third-party system, so that some third-party systems can retry later based on the status. Additionally, the execution of the response node will terminate the workflow's execution, and subsequent nodes will not be executed. If no response node is configured in the entire workflow, the system will automatically respond based on the flow's execution status: returning `200` for successful execution and `500` for failed execution. ## Creating a Response Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Response" node: ![20241210115120](https://static-docs.nocobase.com/20241210115120.png) ## Response Configuration ![20241210115500](https://static-docs.nocobase.com/20241210115500.png) You can use variables from the workflow context in the response body. --- url: /workflow/nodes/sql.md --- # SQL Action ## Introduction In some special scenarios, the simple collection action nodes mentioned above may not be able to handle complex operations. In such cases, you can use the SQL node directly to have the database execute complex SQL statements for data manipulation. The difference between this and directly connecting to the database for SQL operations outside the application is that within a workflow, you can use variables from the process context as parameters in the SQL statement. ## Installation Built-in plugin, no installation required. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add an "SQL Action" node: ![Add SQL Action](https://static-docs.nocobase.com/0ce40a226d7a5bf3717813e27da40e62.png) ## Node Configuration ![SQL Node_Node Configuration](https://static-docs.nocobase.com/20240904002334.png) ### Data Source Select the data source to execute the SQL. The data source must be a database type, such as the main data source, PostgreSQL, or other Sequelize-compatible data sources. ### SQL Content Edit the SQL statement. Currently, only one SQL statement is supported. Insert the required variables using the variable button in the upper right corner of the editor. Before execution, these variables will be replaced with their corresponding values through text substitution. The resulting text will then be used as the final SQL statement and sent to the database for querying. ## Node Execution Result Since `v1.3.15-beta`, the result of a SQL node execution is an array of pure data. Before that, it was the native Sequelize return structure containing query metadata (see: [`sequelize.query()`](https://sequelize.org/api/v6/class/src/sequelize.js~sequelize#instance-method-query)). For example, the following query: ```sql select count(id) from posts; ``` Result before `v1.3.15-beta`: ```json [ [ { "count": 1 } ], { // meta } ] ``` Result after `v1.3.15-beta`: ```json [ { "count": 1 } ] ``` ## FAQ ### How to use the result of a SQL node? If a `SELECT` statement is used, the query result will be saved in the node in Sequelize's JSON format. It can be parsed and used with the [JSON-query](./json-query.md) plugin. ### Does the SQL action trigger collection events? **No**. The SQL action sends the SQL statement directly to the database for processing. The related `CREATE` / `UPDATE` / `DELETE` operations occur in the database, while collection events occur at the Node.js application layer (handled by the ORM), so collection events will not be triggered. --- url: /workflow/nodes/subflow.md --- # Invoke Workflow ## Introduction Used to invoke other workflows from within a workflow. You can use variables from the current workflow as input for the sub-workflow, and use the sub-workflow's output as variables in the current workflow for use in subsequent nodes. The process of invoking a workflow is shown in the figure below: ![20241230134634](https://static-docs.nocobase.com/20241230134634.png) By invoking workflows, you can reuse common process logic, such as sending emails, SMS, etc., or break down a complex workflow into multiple sub-workflows for easier management and maintenance. Essentially, a workflow does not distinguish whether a process is a sub-workflow. Any workflow can be invoked as a sub-workflow by other workflows, and it can also invoke other workflows. All workflows are equal; there is only the relationship of invoking and being invoked. Similarly, the use of invoking a workflow occurs in two places: 1. In the main workflow: As the invoker, it invokes other workflows through the "Invoke Workflow" node. 2. In the sub-workflow: As the invoked party, it saves the variables that need to be output from the current workflow through the "Workflow Output" node, which can be used by subsequent nodes in the workflow that invoked it. ## Create Node In the workflow configuration interface, click the plus ("+") button in the workflow to add an "Invoke Workflow" node: ![Add Invoke Workflow Node](https://static-docs.nocobase.com/20241230001323.png) ## Configure Node ### Select Workflow Select the workflow to invoke. You can use the search box for a quick search: ![Select Workflow](https://static-docs.nocobase.com/20241230001534.png) :::info{title=Tip} * Disabled workflows can also be invoked as sub-workflows. * When the current workflow is in synchronous mode, it can only invoke sub-workflows that are also in synchronous mode. ::: ### Configure Workflow Trigger Variables After selecting a workflow, you also need to configure the trigger's variables as input data to trigger the sub-workflow. You can directly select static data or choose variables from the current workflow: ![Configure Trigger Variables](https://static-docs.nocobase.com/20241230162722.png) Different types of triggers require different variables, which can be configured on the form as needed. ## Workflow Output Node Refer to the content of the [Workflow Output](./output.md) node to configure the output variables of the sub-workflow. ## Using Workflow Output Back in the main workflow, in other nodes below the Invoke Workflow node, when you want to use the output value of the sub-workflow, you can select the result of the Invoke Workflow node. If the sub-workflow outputs a simple value, such as a string, number, boolean, date (date is a string in UTC format), etc., it can be used directly. If it is a complex object (such as an object from a collection), it needs to be mapped through a JSON Parse node before its properties can be used; otherwise, it can only be used as a whole object. If the sub-workflow does not have a Workflow Output node configured, or if it has no output value, then when using the result of the Invoke Workflow node in the main workflow, you will only get a null value (`null`). --- url: /workflow/nodes/update.md --- # Update Data Used to update data in a collection that meets specified conditions. The collection and field assignment parts are the same as the "Create record" node. The main difference with the "Update data" node is the addition of filter conditions and the need to select an update mode. Additionally, the result of the "Update data" node returns the number of rows successfully updated. This can only be viewed in the execution history and cannot be used as a variable in subsequent nodes. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add an "Update data" node: ![Add Update Data Node](https://static-docs.nocobase.com/9ff24d7bc173b3a71decc1f70ca9fb66.png) ## Node Configuration ![Update Data Node Configuration](https://static-docs.nocobase.com/98e0f941c57275fc835f08260d0b2e86.png) ### Collection Select the collection where data needs to be updated. ### Update Mode There are two update modes: * **Bulk update**: Does not trigger collection events for each updated record. It offers better performance and is suitable for large-volume update operations. * **Update one by one**: Triggers collection events for each updated record. However, it may cause performance issues with large volumes of data and should be used with caution. The choice usually depends on the target data for the update and whether other workflow events need to be triggered. If updating a single record based on the primary key, "Update one by one" is recommended. If updating multiple records based on conditions, "Bulk update" is recommended. ### Filter Conditions Similar to the filter conditions in a normal collection query, you can use context variables from the workflow. ### Field Values Similar to field assignment in the "Create record" node, you can use context variables from the workflow or manually enter static values. Note: Data updated by the "Update data" node in a workflow does not automatically handle the "Last modified by" data. You need to configure the value of this field yourself as needed. ## Example For example, when a new "Article" is created, you need to automatically update the "Article Count" field in the "Article Category" collection. This can be achieved using the "Update data" node: ![Update Data Node Example Configuration](https://static-docs.nocobase.com/98e0f941c57275fc835f08260d0b2e86.png) After the workflow is triggered, it will automatically update the "Article Count" field of the "Article Category" collection to the current article count + 1. --- url: /workflow/nodes/variable.md --- # Variable ## Introduction You can declare variables in a flow or assign values to declared variables. This is typically used to store temporary data within the flow. ## Create Node In the workflow configuration interface, click the plus ("+") button in the flow to add a "Variable" node: ![Add Variable Node](https://static-docs.nocobase/53b1e48e777bfff7f2a08271526ef3ee.png) ## Configure Node ### Mode The variable node is similar to variables in programming; it must be declared before it can be used and assigned a value. Therefore, when creating a variable node, you need to select its mode. There are two modes to choose from: ![Select Mode](https://static-docs.nocobase.com/49d8b7b501de6faef6f303262aa14550.png) - Declare a new variable: Creates a new variable. - Assign to an existing variable: Assigns a value to a variable that has been declared earlier in the flow, which is equivalent to modifying the variable's value. When the node being created is the first variable node in the flow, you can only select the declare mode, as there are no variables available for assignment yet. When you choose to assign a value to a declared variable, you also need to select the target variable, which is the node where the variable was declared: ![Select the variable to assign a value to](https://static-docs.nocobase.com/1ce8911548d7347e693d8cc8ac1953eb.png) ### Value The value of a variable can be of any type. It can be a constant, such as a string, number, boolean, or date, or it can be another variable from the flow. In declare mode, setting the variable's value is equivalent to assigning it an initial value. ![Declare initial value](https://static-docs.nocobase.com/4ce2c508986565ad537343013758c6a4.png) In assignment mode, setting the variable's value is equivalent to modifying the value of the declared target variable to a new value. Subsequent uses will retrieve this new value. ![Assign a trigger variable to a declared variable](https://static-docs.nocobase.com/858bae180712ad279ae6a964a77a7659.png) ## Using the Variable's Value In subsequent nodes after the variable node, you can use the variable's value by selecting the declared variable from the "Node Variables" group. For example, in a query node, use the variable's value as a query condition: ![Use variable value as a query filter condition](https://static-docs.nocobase.com/1ca91c295254ff85999a1751499f14bc.png) ## Example A more useful scenario for the variable node is in branches, where new values are calculated or merged with previous values (similar to `reduce`/`concat` in programming), and then used after the branch ends. The following is an example of using a loop branch and a variable node to concatenate a recipient string. First, create a collection-triggered workflow that triggers when "Article" data is updated, and preload the related "Author" association data (to get recipients): ![Configure Trigger](https://static-docs.nocobase.com/93327530a93c695c637d74cdfdcd5cde.png) Then, create a variable node to store the recipient string: ![Recipient variable node](https://static-docs.nocobase.com/d26fa4a7e7ee4f34e0d8392a51c6666e.png) Next, create a loop branch node to iterate through the article's authors and concatenate their recipient information into the recipient variable: ![Loop through authors in the article](https://static-docs.nocobase.com/083fe62c943c17a643dc47ec2872e07c.png) Inside the loop branch, first create a calculation node to concatenate the current author with the already stored author string: ![Concatenate recipient string](https://static-docs.nocobase.com/5d21a990162f32cb8818d27b16fd1bcd.png) After the calculation node, create another variable node. Select the assignment mode, choose the recipient variable node as the assignment target, and select the result of the calculation node as the value: ![Assign the concatenated recipient string to the recipient node](https://static-docs.nocobase.com/fc40ed95dd9b61d924b7ca11b23f9482.png) This way, after the loop branch finishes, the recipient variable will store the recipient string of all the article's authors. Then, after the loop, you can use an HTTP Request node to call a mail sending API, passing the value of the recipient variable as the recipient parameter to the API: ![Send mail to recipients via the request node](https://static-docs.nocobase.com/37f71aa1a63e172bcb2dce10a250947e.png) At this point, a simple bulk email feature has been implemented using a loop and a variable node. --- url: /workflow/triggers/approval.md --- # Approval ## Introduction Approval is a process type specifically designed for human-initiated and human-processed tasks to decide the status of relevant data. It is commonly used for office automation or other manual decision-making processes, such as creating and managing manual workflows for scenarios like "leave requests," "expense reimbursement approvals," and "raw material procurement approvals." The Approval plugin provides a dedicated workflow type (trigger) "Approval (event)" and a dedicated "Approval" node for this process. Combined with NocoBase's unique custom collections and custom blocks, you can quickly and flexibly create and manage various approval scenarios. ## Create a Workflow When creating a workflow, select the "Approval" type to create an approval workflow: ![Approval Trigger_Create Approval Workflow](https://static-docs.nocobase.com/f52dda854f46a669e0c1c7fb487a17ea.png) Afterward, in the workflow configuration interface, click the trigger to open a dialog for more configuration. ## Trigger Configuration ![20251226102619](https://static-docs.nocobase.com/20251226102619.png) ### Bind a Collection NocoBase's Approval plugin is designed for flexibility and can be used with any custom collection. This means the approval configuration does not need to reconfigure the data model but directly reuses an existing collection. Therefore, after entering the trigger configuration, you first need to select a collection to determine which collection's data the process will be for: ![Approval Trigger_Trigger Configuration_Select Collection](https://static-docs.nocobase.com/20251226103223.png) ### Trigger Method When initiating an approval for business data, you can choose from the following two trigger methods: * **Before data is saved** Initiate the approval before the submitted data is saved. This is suitable for scenarios where data should only be saved after approval is granted. In this mode, the data at the time of initiation is temporary and will only be formally saved to the corresponding collection after approval. * **After data is saved** Initiate the approval after the submitted data is saved. This is suitable for scenarios where data can be saved first and then approved. In this mode, the data is already saved in the collection when the approval starts, and any modifications made during the approval process will also be saved. ### Initiation Location You can choose where in the system the approval can be initiated: * **Only in data blocks** You can bind any form block's action for this collection to the workflow to initiate approvals. The process can be handled and tracked in the approval block of a single record. This is typically suitable for business data. * **In both data blocks and the To-do Center** In addition to data blocks, approvals can also be initiated and handled in the global To-do Center. This is typically suitable for administrative data. ### Who can initiate You can configure permissions based on user scope to determine which users can initiate the approval: * **All users** All users in the system can initiate the approval. * **Only selected users** Only users within the specified scope are allowed to initiate the approval. Multiple selections are supported. ![20251226114623](https://static-docs.nocobase.com/20251226114623.png) ### Initiator's Form Interface Configuration Finally, you need to configure the initiator's form interface. This interface will be used for submission actions when initiating from the approval center block and when re-initiating after a withdrawal. Click the configure button to open the dialog: ![Approval Trigger_Trigger Configuration_Initiator Form](https://static-docs.nocobase.com/20251226130239.png) You can add a form for the initiator's interface based on the bound collection, or add descriptive text (Markdown) for prompts and guidance. Adding a form block is mandatory; otherwise, the initiator will not be able to perform any actions upon entering this interface. After adding a form block, just like in a regular form configuration interface, you can add field components from the corresponding collection and arrange them as needed to organize the content to be filled: ![Approval Trigger_Trigger Configuration_Initiator Form_Field Configuration](https://static-docs.nocobase.com/20251226130339.png) In addition to the direct submit button, you can also add a "Save Draft" action button to support temporary storage processes: ![Approval Trigger_Trigger Configuration_Initiator Form_Action Configuration_Save](https://static-docs.nocobase.com/20251226130512.png) If an approval workflow allows the initiator to withdraw, you need to enable the "Withdraw" button in the initiator's interface configuration: ![Approval Trigger_Trigger Configuration_Allow Withdraw](https://static-docs.nocobase.com/20251226130637.png) Once enabled, an approval initiated by this workflow can be withdrawn by the initiator before any approver processes it. However, after any approver in a subsequent approval node has processed it, it can no longer be withdrawn. :::info{title=Note} After enabling or deleting the withdraw button, you need to click save and submit in the trigger configuration dialog for the changes to take effect. ::: ### "My application" Card 2.0+ Used to configure the task cards in the "My Submissions" list of the To-do Center. ![20260213005957](https://static-docs.nocobase.com/20260213005957.png) You can freely configure the business fields (except association fields) or approval-related information you want to display on the card. After the approval request is created, the customized task card will be visible in the To-do Center list: ![20260213010228](https://static-docs.nocobase.com/20260213010228.png) ### Record Display Mode in the Flow * **Snapshot** The record state seen by the applicant and approvers when they enter the process. After submission, they only see the records they modified—they will not see updates made by others later. * **Latest** The applicant and approvers always see the latest version of the record throughout the process, regardless of the record's state before their action. After the process ends, they will see the final version of the record. ## Approval Node In an approval workflow, you need to use the dedicated "Approval" node to configure the operational logic for approvers to process (approve, reject, or return) the initiated approval. The "Approval" node can only be used in approval workflows. Refer to [Approval Node](../nodes/approval.md) for details. :::info{title=Note} If an approval workflow does not contain any "Approval" nodes, the workflow will be automatically approved. ::: ## Configure Approval Initiation After configuring and enabling an approval workflow, you can bind it to the submit button of the corresponding collection's form, allowing users to initiate an approval upon submission: ![Initiate Approval_Bind Workflow](https://static-docs.nocobase.com/20251226110710.png) After that, when a user submits this form, the corresponding approval workflow will be triggered. The submitted data is not only saved in the corresponding collection but is also snapshotted into the approval flow for subsequent approvers to review. :::info{title=Note} Currently, the button to initiate an approval only supports the "Submit" (or "Save") button in a create or update form. It does not support the "Trigger workflow" button (which can only be bound to "Custom action event"). ::: ## To-do Center The To-do Center provides a unified entry point for users to view and process tasks. Approvals initiated by the current user and pending tasks can be accessed through the To-do Center in the top toolbar, and different types of tasks can be viewed through the navigation on the left. ![20250310161203](https://static-docs.nocobase.com/20250310161203.png) ### My Submissions #### View Submitted Approvals ![20250310161609](https://static-docs.nocobase.com/20250310161609.png) #### Directly Initiate a New Approval ![20250310161658](https://static-docs.nocobase.com/20250310161658.png) ### My To-dos #### To-do List ![20250310161934](https://static-docs.nocobase.com/20250310161934.png) #### To-do Details ![20250310162111](https://static-docs.nocobase.com/20250310162111.png) ## HTTP API ### Initiator #### Initiate from Collection To initiate from a data block, you can make a call like this (using the create button of the `posts` collection as an example): ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Hello, world!", "content": "This is a test post." }' "http://localhost:3000/api/posts:create?triggerWorkflows=workflowKey" ``` The URL parameter `triggerWorkflows` is the workflow's key; multiple workflow keys are separated by commas. This key can be obtained by hovering the mouse over the workflow name at the top of the workflow canvas: ![Workflow_Key_View_Method](https://static-docs.nocobase.com/20240426135108.png) Upon a successful call, the approval workflow for the corresponding `posts` collection will be triggered. :::info{title="Note"} Since external calls also need to be based on user identity, when calling via HTTP API, authentication information must be provided, including the `Authorization` header or the `token` parameter (the token obtained upon login), and the `X-Role` header (the user's current role name). ::: If you need to trigger an event for one-to-one related data in this action (one-to-many is not yet supported), you can use `!` in the parameter to specify the trigger data for the association field: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Hello, world!", "content": "This is a test post.", "category": { "title": "Test category" } }' "http://localhost:3000/api/posts:create?triggerWorkflows=workflowKey!category" ``` Upon a successful call, the approval event for the corresponding `categories` collection will be triggered. :::info{title="Note"} When triggering an after-action event via HTTP API, ensure the workflow is enabled and the collection configuration matches; otherwise, the call may not succeed or may result in an error. ::: #### Initiate from Approval Center ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "collectionName": "", "workflowId": , "data": { "": "" }, "status": , }' "http://localhost:3000/api/approvals:create" ``` **Parameters** * `collectionName`: The name of the target collection for initiating the approval. Required. * `workflowId`: The ID of the workflow used to initiate the approval. Required. * `data`: The fields of the collection record created when initiating the approval. Required. * `status`: The status of the record created when initiating the approval. Required. Possible values include: * `0`: Draft, indicates saving without submitting for approval. * `2`: Submit for approval, indicates the initiator submits the approval request, entering the approval process. #### Save and Submit When an initiated (or withdrawn) approval is in a draft state, you can save or submit it again through the following API: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "data": { "": "" }, "status": 2 }' "http://localhost:3000/api/approvals:update/" ``` #### Get List of Submitted Approvals ```bash curl -X GET -H 'Authorization: Bearer ' -H 'X-Role: ' \ "http://localhost:3000/api/approvals:listMine" ``` #### Withdraw The initiator can withdraw a record currently in approval through the following API: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ "http://localhost:3000/api/approvals:withdraw/" ``` **Parameters** * ``: The ID of the approval record to be withdrawn. Required. ### Approver After the approval workflow enters an approval node, a to-do task is created for the current approver. The approver can complete the approval task through the interface or by calling the HTTP API. #### Get Approval Records To-do tasks are approval records. You can get all of the current user's approval records through the following API: ```bash curl -X GET -H 'Authorization: Bearer ' \ "http://localhost:3000/api/approvalRecords:listMine" ``` Here, `approvalRecords` is a collection resource, so you can use common query conditions such as `filter`, `sort`, `pageSize`, and `page`. #### Get a Single Approval Record ```bash curl -X GET -H 'Authorization: Bearer ' \ "http://localhost:3000/api/approvalRecords:get/" ``` #### Approve and Reject ```bash curl -X POST -H 'Authorization: Bearer ' -d \ '{ "status": 2, "comment": "Looks good to me.", "data": { "": "" } }' "http://localhost:3000/api/approvalRecords:submit/" ``` **Parameters** * ``: The ID of the record to be processed. Required. * `status`: The status of the approval process. `2` for "Approve", `-1` for "Reject". Required. * `comment`: Remarks for the approval process. Optional. * `data`: Modifications to the collection record at the current approval node after approval. Optional (only effective upon approval). #### Return v1.9.0+ Before v1.9.0, returning used the same API as "Approve" and "Reject," with `"status": 1` representing a return. Starting from v1.9.0, returning has a separate API: ```bash curl -X POST -H 'Authorization: Bearer ' -d \ '{ "returnToNodeKey": "", }' "http://localhost:3000/api/approvalRecords:return/" ``` **Parameters** * ``: The ID of the record to be processed. Required. * `returnToNodeKey`: The key of the target node to return to. Optional. When a range of returnable nodes is configured in the node, this parameter can be used to specify which node to return to. If not configured, this parameter does not need to be passed, and it will default to returning to the starting point for the initiator to resubmit. #### Delegate ```bash curl -X POST -H 'Authorization: Bearer ' -d \ '{ "assignee": , }' "http://localhost:3000/api/approvalRecords:delegate/" ``` **Parameters** * ``: The ID of the record to be processed. Required. * `assignee`: The ID of the user to delegate to. Required. #### Add Signer ```bash curl -X POST -H 'Authorization: Bearer ' -d \ '{ "assignees": [], "order": , }' "http://localhost:3000/api/approvalRecords:add/" ``` **Parameters** * ``: The ID of the record to be processed. Required. * `assignees`: A list of user IDs to add as signers. Required. * `order`: The order of the added signer. `-1` means before "me", `1` means after "me". --- url: /workflow/triggers/collection.md --- # Collection Events ## Introduction Triggers of the collection event type will listen for create, update, and delete events on a collection. When a data operation on that collection occurs and meets the configured conditions, it triggers the corresponding workflow. For example, scenarios like deducting product inventory after a new order is created, or waiting for manual review after a new comment is added. ## Basic Usage There are several types of collection changes: 1. After creating data. 2. After updating data. 3. After creating or updating data. 4. After deleting data. ![Collection Event_Trigger Timing Selection](https://static-docs.nocobase.com/81275602742deb71e0c830eb97aa612c.png) You can choose the trigger timing based on different business needs. When the selected change includes updating the collection, you can also specify the fields that have changed. The trigger condition is met only when the selected fields change. If no fields are selected, it means a change in any field can trigger the workflow. ![Collection Event_Select Changed Fields](https://static-docs.nocobase.com/874a1475f01298b3c00267b2b4674611.png) More specifically, you can configure condition rules for each field of the triggering data row. The trigger will only fire when the fields meet the corresponding conditions. ![Collection Event_Configure Data Conditions](https://static-docs.nocobase.com/264ae3835dcd75cee0eef7812c11fe0c.png) After a collection event is triggered, the data row that generated the event will be injected into the execution plan as trigger context data, to be used as variables by subsequent nodes in the workflow. However, when subsequent nodes need to use the relationship fields of this data, you need to configure preloading of the relationship data first. The selected relationship data will be injected into the context along with the trigger and can be selected and used hierarchically. ## Related Tips ### Triggering by Bulk Data Operations is Not Currently Supported Collection events do not currently support triggering by bulk data operations. For example, when creating an article and simultaneously adding multiple tags for that article (one-to-many relationship data), only the workflow for creating the article will be triggered. The simultaneously created multiple tags will not trigger the workflow for creating tags. When associating or adding many-to-many relationship data, the workflow for the intermediate collection will not be triggered either. ### Data Operations Outside the Application Will Not Trigger Events Operations on collections via HTTP API calls to the application's interface can also trigger corresponding events. However, if data changes are made directly through database operations instead of through the NocoBase application, the corresponding events cannot be triggered. For example, native database triggers will not be associated with workflows in the application. Additionally, using the SQL action node to operate on the database is equivalent to direct database operations and will not trigger collection events. ### External Data Sources Workflows have supported external data sources since version `0.20`. If you are using an external data source plugin and the collection event is configured for an external data source, as long as the data operations on that data source are performed within the application (such as user creation, updates, and workflow data operations), the corresponding collection events can be triggered. However, if the data changes are made through other systems or directly in the external database, collection events cannot be triggered. ## Example Let's take the scenario of calculating the total price and deducting inventory after a new order is created as an example. First, we create a Products collection and an Orders collection with the following data models: | Field Name | Field Type | | --- | --- | | Product Name | Single Line Text | | Price | Number | | Inventory | Integer | | Field Name | Field Type | | --- | --- | | Order ID | Sequence | | Order Product | Many-to-One (Products) | | Order Total | Number | And add some basic product data: | Product Name | Price | Inventory | | --- | --- | --- | | iPhone 14 Pro | 7999 | 10 | | iPhone 13 Pro | 5999 | 0 | Then, create a workflow based on the Orders collection event: ![Collection Event_Example_New Order Trigger](https://static-docs.nocobase.com/094392a870dddc65aeb20357f62ddc08.png) Here are some of the configuration options: - Collection: Select the "Orders" collection. - Trigger timing: Select "After creating data". - Trigger conditions: Leave blank. - Preload relationship data: Check "Products". Then, configure other nodes according to the workflow logic: check if the product inventory is greater than 0. If it is, deduct the inventory; otherwise, the order is invalid and should be deleted: ![Collection Event_Example_New Order Workflow Orchestration](https://static-docs.nocobase.com/7713ea1aaa0f52a0dc3c92aba5e58f05.png) The configuration of nodes will be explained in detail in the documentation for specific node types. Enable this workflow and test it by creating a new order through the interface. After placing an order for "iPhone 14 Pro", the inventory of the corresponding product will be reduced to 9. If an order is placed for "iPhone 13 Pro", the order will be deleted due to insufficient inventory. ![Collection Event_Example_New Order Execution Result](https://static-docs.nocobase.com/24cbe51e24ba4804b3bd48d99415c54f.png) --- url: /workflow/triggers/custom-action.md --- # Custom Action Event ## Introduction NocoBase has built-in common data actions (Add New, Delete, Edit, View, etc.). When these actions cannot meet complex business needs, you can use the Custom Action Event in a workflow and bind it to a "Trigger Workflow" button in a page block. When a user clicks the button, it will trigger a custom action workflow. ## Create a Workflow When creating a workflow, select "Custom Action Event": ![Create "Custom Action Event" workflow](https://static-docs.nocobase.com/20240509091820.png) ## Trigger Configuration ### Context Type > v1.6.0+ The context type determines which block buttons the workflow can be bound to: * No context: A global event that can be bound to action buttons in action panels and data blocks; * Single record: Can be bound to action buttons in data blocks such as table rows, forms, and details; * Multiple records: Can be bound to bulk action buttons in a table. ![Trigger Configuration_Context Type](https://static-docs.nocobase.com/20250215135808.png) ### Collection When the context type is Single record or Multiple records, you need to select the collection to bind the data model to: ![Trigger Configuration_Select Collection](https://static-docs.nocobase.com/20250215135919.png) ### Association Data to be Used If you need to use the association data of the triggering data row in the workflow, you can select deep association fields here: ![Trigger Configuration_Select Association Data to be Used](https://static-docs.nocobase.com/20250215135955.png) These fields will be automatically preloaded into the workflow context after the event is triggered, making them available for use in the workflow. ## Action Configuration The configuration of action buttons in different blocks varies depending on the context type configured in the workflow. ### No Context > v1.6.0+ In the action panel and other data blocks, you can add a "Trigger Workflow" button: ![Add Action Button to Block_Action Panel](https://static-docs.nocobase.com/20250215221738.png) ![Add Action Button to Block_Calendar](https://static-docs.nocobase.com/20250215221942.png) ![Add Action Button to Block_Gantt Chart](https://static-docs.nocobase.com/20250215221810.png) After adding the button, bind the previously created no-context workflow. Here is an example using a button in the action panel: ![Bind Workflow to Button_Action Panel](https://static-docs.nocobase.com/20250215222120.png) ![Select Workflow to Bind_No Context](https://static-docs.nocobase.com/20250215222234.png) ### Single Record In any data block, a "Trigger Workflow" button can be added to the action bar for a single record, such as in forms, table rows, and details: ![Add Action Button to Block_Form](https://static-docs.nocobase.com/20240509165428.png) ![Add Action Button to Block_Table Row](https://static-docs.nocobase.com/20240509165340.png) ![Add Action Button to Block_Details](https://static-docs.nocobase.com/20240509165545.png) After adding the button, bind the previously created workflow: ![Bind Workflow to Button](https://static-docs.nocobase.com/20240509165631.png) ![Select Workflow to Bind](https://static-docs.nocobase.com/20240509165658.png) Afterward, clicking this button will trigger the custom action event: ![Result of Clicking the Button](https://static-docs.nocobase.com/20240509170453.png) ### Multiple Records > v1.6.0+ In the action bar of a table block, when adding a "Trigger Workflow" button, there is an additional option to select the context type: "No context" or "Multiple records": ![Add Action Button to Block_Table](https://static-docs.nocobase.com/20250215222507.png) When "No context" is selected, it is a global event and can only be bound to no-context workflows. When "Multiple records" is selected, you can bind a multiple-records workflow, which can be used for bulk actions after selecting multiple records (currently only supported by tables). The available workflows are limited to those configured to match the collection of the current data block: ![20250215224436](https://static-docs.nocobase.com/20250215224436.png) When clicking the button to trigger, some data rows in the table must be checked; otherwise, the workflow will not be triggered: ![20250215224736](https://static-docs.nocobase.com/20250215224736.png) ## Example For example, we have a "Samples" collection. For samples with a status of "Collected", we need to provide a "Submit for Inspection" action. This action will first check the basic information of the sample, then generate an "Inspection Record", and finally change the sample's status to "Submitted". This series of processes cannot be completed with simple CRUD button clicks, so a custom action event can be used to implement it. First, create a "Samples" collection and an "Inspection Records" collection, and enter some basic test data into the Samples collection: ![Example_Samples Collection](https://static-docs.nocobase.com/20240509172234.png) Then, create a "Custom Action Event" workflow. If you need timely feedback from the operation process, you can choose synchronous mode (in synchronous mode, you cannot use asynchronous nodes like manual processing): ![Example_Create Workflow](https://static-docs.nocobase.com/20240509173106.png) In the trigger configuration, select "Samples" for the collection: ![Example_Trigger Configuration](https://static-docs.nocobase.com/20240509173148.png) Arrange the logic in the process according to business requirements. For example, allow submission for inspection only when the indicator parameter is greater than `90`; otherwise, display a relevant message: ![Example_Business Logic Arrangement](https://static-docs.nocobase.com/20240509174159.png) :::info{title=Tip} The "[Response Message](../nodes/response-message.md)" node can be used in synchronous custom action events to return a prompt message to the client. It cannot be used in asynchronous mode. ::: After configuring and enabling the workflow, return to the table interface and add a "Trigger Workflow" button in the action column of the table: ![Example_Add Action Button](https://static-docs.nocobase.com/20240509174525.png) Then, in the button's configuration menu, choose to bind a workflow and open the configuration pop-up: ![Example_Open Bind Workflow Pop-up](https://static-docs.nocobase.com/20240509174633.png) Add the previously enabled workflow: ![Example_Select Workflow](https://static-docs.nocobase.com/20240509174723.png) After submitting, change the button text to the action name, such as "Submit for Inspection". The configuration process is now complete. To use it, select any sample data in the table and click the "Submit for Inspection" button to trigger the custom action event. As per the logic arranged earlier, if the sample's indicator parameter is less than 90, the following prompt will be displayed after clicking: ![Example_Indicator Does Not Meet Submission Criteria](https://static-docs.nocobase.com/20240509175026.png) If the indicator parameter is greater than 90, the process will execute normally, generating an "Inspection Record" and changing the sample's status to "Submitted": ![Example_Submission Successful](https://static-docs.nocobase.com/20240509175247.png) At this point, a simple custom action event is complete. Similarly, for businesses with complex operations like order processing or report submission, custom action events can be used for implementation. ## External Call The triggering of custom action events is not limited to user interface actions; it can also be triggered via HTTP API calls. Specifically, custom action events provide a new action type for all collection actions to trigger workflows: `trigger`, which can be called using NocoBase's standard action API. :::info{title="Tip"} Since external calls also need to be based on user identity, when calling via HTTP API, just like requests sent from the regular interface, you need to provide authentication information. This includes the `Authorization` request header or `token` parameter (the token obtained upon login), and the `X-Role` request header (the user's current role name). ::: ### No Context No-context workflows need to be triggered on the workflows resource: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' \ "http://localhost:3000/api/workflows:trigger?triggerWorkflows=workflowKey" ``` ### Single Record A workflow triggered by a button, as in the example, can be called like this: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' \ "http://localhost:3000/api/samples:trigger/<:id>?triggerWorkflows=workflowKey" ``` Since this action is for a single record, when calling it on existing data, you need to specify the ID of the data row, replacing the `<:id>` part in the URL. If it's called for a form (such as for creating or updating), you can omit the ID for a form that creates new data, but you must pass the submitted data as the execution context: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Sample 1", "id": 91 }' "http://localhost:3000/api/samples:trigger?triggerWorkflows=workflowKey" ``` For an update form, you need to pass both the ID of the data row and the updated data: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Sample 1", "id": 91 }' "http://localhost:3000/api/samples:trigger/<:id>?triggerWorkflows=workflowKey" ``` If both an ID and data are passed, the data row corresponding to the ID will be loaded first, and then the properties from the passed data object will be used to overwrite the original data row to get the final trigger data context. :::warning{title="Note"} If association data is passed, it will also be overwritten. Be especially cautious when handling incoming data if preloading of association data items is configured, to avoid unexpected overwrites of association data. ::: Additionally, the URL parameter `triggerWorkflows` is the workflow key; multiple workflow keys are separated by commas. This key can be obtained by hovering the mouse over the workflow name at the top of the workflow canvas: ![Workflow_Key_View Method](https://static-docs.nocobase.com/20240426135108.png) After a successful call, the custom action event for the corresponding `samples` collection will be triggered. :::info{title="Tip"} When triggering an action event via an HTTP API call, you also need to pay attention to the workflow's enabled status and whether the collection configuration matches; otherwise, the call may not succeed or may result in an error. ::: ### Multiple Records Similar to the single record call, but the passed data only needs multiple primary key parameters (`filterByTk[]`), and no data part is required: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' \ "http://localhost:3000/api/samples:trigger?filterByTk[]=1&filterByTk[]=2&triggerWorkflows=workflowKey" ``` This call will trigger a custom action event in multiple records mode, using the data with IDs 1 and 2 as the trigger context. --- url: /workflow/triggers/index.md --- # Overview A trigger is the entry point for a workflow. When an event that meets the trigger's conditions occurs while the application is running, the workflow will be triggered and executed. The type of trigger is also the type of workflow, selected when creating the workflow, and cannot be modified after creation. The currently supported trigger types are as follows: - [Collection Events](./collection) (Built-in) - [Schedule](./schedule) (Built-in) - [Before Action](./pre-action) (Provided by the @nocobase/plugin-workflow-request-interceptor plugin) - [After Action](./post-action) (Provided by the @nocobase/plugin-workflow-action-trigger plugin) - [Custom Action](./custom-action) (Provided by the @nocobase/plugin-workflow-custom-action-trigger plugin) - [Approval](./approval) (Provided by the @nocobase/plugin-workflow-approval plugin) - [Webhook](./webhook) (Provided by the @nocobase/plugin-workflow-webhook plugin) The timing of when each event is triggered is shown in the figure below: ![Workflow Events](https://static-docs.nocobase.com/20251029221709.png) For example, when a user submits a form, or when data in a collection changes due to user action or a program call, or when a scheduled task reaches its execution time, a configured workflow can be triggered. Data-related triggers (such as actions, collection events) usually carry trigger context data. This data acts as variables and can be used by nodes in the workflow as processing parameters to achieve automated data processing. For example, when a user submits a form, if the submit button is bound to a workflow, that workflow will be triggered and executed. The submitted data will be injected into the execution plan's context environment for subsequent nodes to use as variables. After creating a workflow, on the workflow view page, the trigger is displayed as an entry node at the beginning of the process. Clicking on this card will open the configuration drawer. Depending on the type of trigger, you can configure its relevant conditions. ![Trigger_Entry Node](https://static-docs.nocobase.com/20251029222231.png) --- url: /workflow/triggers/post-action.md --- # Post-Action Event ## Introduction All data changes made by users in the system are typically accomplished through an action, usually in the form of clicking a button. This button could be a submit button in a form or an action button in a data block. The post-action event is used to bind related workflows to the actions of these buttons, so that a specific process is triggered after the user's action is successfully completed. For example, when adding or updating data, users can configure the "Bind workflow" option for a button. After the action is completed, the bound workflow will be triggered. At the implementation level, since post-action event handling is at the middleware layer (Koa's middleware), HTTP API calls to NocoBase can also trigger defined post-action events. ## Installation This is a built-in plugin, no installation is required. ## Trigger Configuration ### Create Workflow When creating a workflow, select "Post-Action Event" as the type: ![Create Workflow_Post-Action Event Trigger](https://static-docs.nocobase.com/13c87035ec1bb7332514676d3e896007.png) ### Execution Mode For post-action events, you can also choose the execution mode as "Synchronous" or "Asynchronous" when creating it: ![Create Workflow_Select Synchronous or Asynchronous](https://static-docs.nocobase.com/bc83525c7e539d578f9e2e20baf9ab69.png) If the process needs to be executed and returned immediately after the user's action, you can use synchronous mode; otherwise, the default is asynchronous mode. In asynchronous mode, the action is completed immediately after the workflow is triggered, and the workflow will be executed sequentially in the application's background queue. ### Configure Collection Enter the workflow canvas, click the trigger to open the configuration popup, and first select the collection to bind: ![Workflow Configuration_Select Collection](https://static-docs.nocobase.com/35c49a91eba731127edcf76719c97634.png) ### Select Trigger Mode Then select the trigger mode, which can be either local or global: ![Workflow Configuration_Select Trigger Mode](https://static-docs.nocobase.com/317809c48b2f2a2d38aedc7d08abdadc.png) Where: * Local mode is only triggered on action buttons that have this workflow bound. Clicking buttons without this workflow bound will not trigger it. You can decide whether to bind this workflow based on whether forms with different purposes should trigger the same process. * Global mode is triggered on all configured action buttons of the collection, regardless of which form they come from, and there is no need to bind the corresponding workflow. In local mode, the action buttons that currently support binding are as follows: * "Submit" and "Save" buttons in the add form. * "Submit" and "Save" buttons in the update form. * "Update data" button in data rows (table, list, kanban, etc.). ### Select Action Type If you choose global mode, you also need to select the action type. Currently, "Create data action" and "Update data action" are supported. Both actions trigger the workflow after the action is successful. ### Select Preloaded Association Data If you need to use the associated data of the triggering data in subsequent processes, you can select the association fields to be preloaded: ![Workflow Configuration_Preload Association](https://static-docs.nocobase.com/5cded063509c7ba1d34f49bec8d68227.png) After triggering, you can directly use this associated data in the process. ## Action Configuration For actions in local trigger mode, after the workflow is configured, you need to return to the user interface and bind the workflow to the form action button of the corresponding data block. Workflows configured for the "Submit" button (including the "Save data" button) will be triggered after the user submits the corresponding form and the data action is completed. ![Post-Action Event_Submit Button](https://static-docs.nocobase.com/ae12d219b8400d75b395880ec4cb2bda.png) Select "Bind workflow" from the button configuration menu to open the binding configuration popup. In the popup, you can configure any number of workflows to be triggered. If none are configured, it means no trigger is needed. For each workflow, you first need to specify whether the trigger data is the data of the entire form or the data of a certain association field in the form. Then, based on the collection corresponding to the selected data model, select the form workflow that has been configured to match that collection model. ![Post-Action Event_Bind Workflow Configuration_Context Selection](https://static-docs.nocobase.com/358315fc175849a7fbadbe3276ac6fed.png) ![Post-Action Event_Bind Workflow Configuration_Workflow Selection](https://static-docs.nocobase.com/175a71a61b93540cce62a1cb124eb0b5.png) :::info{title="Note"} The workflow must be enabled before it can be selected in the interface above. ::: ## Example Here is a demonstration using the create action. Suppose there is a "Reimbursement Application" scenario. We need to perform an automatic review of the amount and a manual review for amounts exceeding the limit after an employee submits a reimbursement request. Only applications that pass the review are approved and then handed over to the finance department for processing. First, we can create a "Reimbursement" collection with the following fields: - Project Name: Single Line Text - Applicant: Many-to-One (User) - Amount: Number - Status: Single Select ("Approved", "Processed") Then, create a "Post-Action Event" type workflow and configure the collection model in the trigger to be the "Reimbursement" collection: ![Example_Trigger Configuration_Select Collection](https://static-docs.nocobase.com/6e1abb5c3e1198038676115943714f07.png) Set the workflow to the enabled state, and we will come back to configure the specific processing nodes of the process later. Then, we create a table block for the "Reimbursement" collection on the interface, add an "Add" button to the toolbar, and configure the corresponding form fields. In the configuration options of the form's "Submit" action button, open the "Bind workflow" configuration dialog, select the entire form data as the context, and select the workflow we created earlier: ![Example_Form Button Configuration_Bind Workflow](https://static-docs.nocobase.com/fc00bdcdb975bb8850e5cab235f854f3.png) After the form configuration is complete, return to the workflow's logic orchestration. For example, we require a manual review by an administrator when the amount is greater than 500, otherwise it is directly approved. After approval, a reimbursement record is created and further processed by the finance department (omitted). ![Example_Processing Flow](https://static-docs.nocobase.com/059e8e3d5ffb34cc2da6880fa3dc490b.png) Ignoring the subsequent processing by the finance department, the configuration of the reimbursement application process is now complete. When an employee fills out and submits a reimbursement application, the corresponding workflow will be triggered. If the expense amount is less than 500, a record will be automatically created and await further processing by finance. Otherwise, it will be reviewed by a supervisor, and after approval, a record will also be created and handed over to finance. The process in this example can also be configured on a regular "Submit" button. You can decide whether to create a record first before executing subsequent processes based on the specific business scenario. ## External Call The triggering of post-action events is not limited to user interface operations; it can also be triggered through HTTP API calls. :::info{title="Note"} When triggering a post-action event via an HTTP API call, you also need to pay attention to the enabled state of the workflow and whether the collection configuration matches, otherwise the call may not succeed or an error may occur. ::: For workflows locally bound to an action button, you can call it like this (using the create button of the `posts` collection as an example): ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Hello, world!", "content": "This is a test post." }' "http://localhost:3000/api/posts:create?triggerWorkflows=workflowKey" ``` Where the URL parameter `triggerWorkflows` is the key of the workflow, with multiple workflows separated by commas. This key can be obtained by hovering the mouse over the workflow name at the top of the workflow canvas: ![Workflow_Key_View Method](https://static-docs.nocobase.com/20240426135108.png) After the above call is successful, the post-action event of the corresponding `posts` collection will be triggered. :::info{title="Note"} Since external calls also need to be based on user identity, when calling via HTTP API, just like requests sent from the normal interface, authentication information must be provided, including the `Authorization` request header or `token` parameter (the token obtained upon login), and the `X-Role` request header (the user's current role name). ::: If you need to trigger an event for a to-one relationship data in this action (to-many is not yet supported), you can use `!` in the parameter to specify the trigger data of the association field: ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Hello, world!", "content": "This is a test post.", "category": { "title": "Test category" } }' "http://localhost:3000/api/posts:create?triggerWorkflows=workflowKey!category" ``` After the above call is successful, the post-action event of the corresponding `categories` collection will be triggered. :::info{title="Note"} If the event is configured in global mode, you do not need to use the URL parameter `triggerWorkflows` to specify the corresponding workflow. Simply calling the corresponding collection action will trigger it. ::: ## FAQ ### Difference from Pre-Action Event * Pre-Action Event: Triggered before an action (such as add, update, etc.) is executed. Before the action is executed, the requested data can be validated or processed in the workflow. If the workflow is terminated (the request is intercepted), the action (add, update, etc.) will not be executed. * Post-Action Event: Triggered after a user's action is successful. At this point, the data has been successfully submitted and saved to the database, and related processes can continue to be processed based on the successful result. As shown in the figure below: ![Action Execution Order](https://static-docs.nocobase.com/20251219234806.png) ### Difference from Collection Event Post-action events and collection events are similar in that they are both processes triggered after data changes. However, their implementation levels are different. Post-action events are at the API level, while collection events are for data changes in the collection. Collection events are closer to the system's underlying layer. In some cases, a data change caused by one event may trigger another event, creating a chain reaction. Especially when data in some associated collections also changes during the operation of the current collection, events related to the associated collection can also be triggered. The triggering of collection events does not include user-related information. In contrast, post-action events are closer to the user end and are the result of user actions. The context of the workflow will also contain user-related information, making it suitable for handling processes related to user actions. In NocoBase's future design, more post-action events that can be used for triggering may be expanded, so **it is more recommended to use post-action events** to handle processes where data changes are caused by user actions. Another difference is that post-action events can be locally bound to specific form buttons. If there are multiple forms, some form submissions can trigger the event while others do not. Collection events, on the other hand, are for data changes in the entire collection and cannot be locally bound. --- url: /workflow/triggers/pre-action.md --- # Before Action Event ## Introduction The Before Action Event plugin provides an interception mechanism for actions, which can be triggered after a request for create, update, or delete action is submitted but before it is processed. If an "End workflow" node is executed in the triggered workflow, or if any other node fails to execute (due to an error or other incompletion), the form action will be intercepted. Otherwise, the intended action will be executed normally. Using it with the "Response message" node allows you to configure a response message to be returned to the client, providing appropriate prompts. Before action events can be used for business validation or logic checks to approve or intercept create, update, and delete action requests submitted by the client. ## Trigger Configuration ### Create Trigger When creating a workflow, select the type "Before action event": ![Create Before Action Event](https://static-docs.nocobase.com/2add03f2bdb0a836baae5fe9864fc4b6.png) ### Select Collection In the trigger of an interception workflow, the first thing to configure is the collection corresponding to the action: ![Interceptor Event Configuration_Collection](https://static-docs.nocobase.com/8f7122caca8159d334cf776f838d53d6.png) Then select the interception mode. You can choose to intercept only the action button bound to this workflow, or to intercept all selected actions for this collection (regardless of which form it comes from, and without needing to bind the corresponding workflow): ### Interception Mode ![Interceptor Event Configuration_Interception Mode](https://static-docs.nocobase.com/145a7f7c3ba440bb6ca93a5ee84f16e2.png) Currently supported action types are "Create", "Update", and "Delete". Multiple action types can be selected simultaneously. ## Action Configuration If the "Trigger interception only when a form bound to this workflow is submitted" mode is selected in the trigger configuration, you also need to go back to the form interface and bind this workflow to the corresponding action button: ![Add Order_Bind Workflow](https://static-docs.nocobase.com/bae3931e60f9bcc51bbc222e40e891e5.png) In the bind workflow configuration, select the corresponding workflow. Usually, the default context for triggering data, "Entire form data", is sufficient: ![Select Workflow to Bind](https://static-docs.nocobase.com/78e2f023029bd570c91ee4cd19b7a0a7.png) :::info{title=Note} The buttons that can be bound to a Before Action Event currently only support "Submit" (or "Save"), "Update data", and "Delete" buttons in create or update forms. The "Trigger workflow" button is not supported (it can only be bound to an "After action event"). ::: ## Conditions for Interception In a "Before action event", there are two conditions that will cause the corresponding action to be intercepted: 1. The workflow executes to any "End workflow" node. Similar to the previous instructions, when the data that triggered the workflow does not meet the preset conditions in a "Condition" node, it will enter the "No" branch and execute the "End workflow" node. At this point, the workflow will end, and the requested action will be intercepted. 2. Any node in the workflow fails to execute, including execution errors or other exceptions. In this case, the workflow will end with a corresponding status, and the requested action will also be intercepted. For example, if the workflow calls external data via an "HTTP request" and the request fails, the workflow will end with a failed status and will also intercept the corresponding action request. After the interception conditions are met, the corresponding action will no longer be executed. For example, if an order submission is intercepted, the corresponding order data will not be created. ## Related Parameters for the Corresponding Action In a "Before action event" type workflow, different data from the trigger can be used as variables in the workflow for different actions: | Action Type \ Variable | "Operator" | "Operator role identifier" | Action parameter: "ID" | Action parameter: "Submitted data object" | | ---------------------- | ---------- | -------------------------- | ---------------------- | ----------------------------------------- | | Create a record | ✓ | ✓ | - | ✓ | | Update a record | ✓ | ✓ | ✓ | ✓ | | Delete single or multiple records | ✓ | ✓ | ✓ | - | :::info{title=Note} The "Trigger data / Action parameters / Submitted data object" variable in a Before Action Event is not the actual data from the database, but rather the parameters submitted with the action. If you need the actual data from the database, you must query for it using a "Query data" node within the workflow. Additionally, for a delete action, the "ID" in the action parameters is a single value when targeting a single record, but it is an array when targeting multiple records. ::: ## Output Response Message After configuring the trigger, you can customize the relevant judgment logic in the workflow. Typically, you will use the branch mode of the "Condition" node to decide whether to "End workflow" and return a preset "Response message" based on the results of specific business conditions: ![Interceptor Workflow Configuration](https://static-docs.nocobase.com/cfddda5d8012fd3d0ca09f04ea610539.png) At this point, the configuration of the corresponding workflow is complete. You can now try to submit data that does not meet the conditions configured in the workflow's condition node to trigger the interception logic. You will then see the returned response message: ![Error Response Message](https://static-docs.nocobase.com/06bd4a6b6ec499c853f0c39987f63a6a.png) ### Response Message Status If the "End workflow" node is configured to exit with a "Success" status, the action request will still be intercepted when this node is executed, but the returned response message will be displayed with a "Success" status (instead of "Error"): ![Success Status Response Message](https://static-docs.nocobase.com/9559bbf56067144759451294b18c790e.png) ## Example Combining the basic instructions above, let's take an "Order Submission" scenario as an example. Suppose we need to check the inventory of all products selected by the user when they submit an order. If the inventory of any selected product is insufficient, the order submission is intercepted, and a corresponding prompt message is returned. The workflow will loop through and check each product until the inventory for all products is sufficient, at which point it will proceed and create the order data for the user. Other steps are the same as in the instructions. However, since an order involves multiple products, in addition to adding a many-to-many relationship "Order" <-- M:1 -- "Order Item" -- 1:M --> "Product" in the data model, you also need to add a "Loop" node in the "Before action event" workflow to iteratively check if the inventory of each product is sufficient: ![Example_Loop Check Workflow](https://static-docs.nocobase.com/8307de47d5629595ab6cf00f8aa898e3.png) The object for the loop is selected as the "Order Item" array from the submitted order data: ![Example_Loop Object Configuration](https://static-docs.nocobase.com/ed662b54cc1f5425e2b472053f89baba.png) The condition node within the loop is used to determine if the inventory of the current product object in the loop is sufficient: ![Example_Condition in Loop](https://static-docs.nocobase.com/4af91112934b0a04a4ce55e657c0833b.png) Other configurations are the same as in the basic usage. When the order is finally submitted, if any product has insufficient inventory, the order submission will be intercepted, and a corresponding prompt message will be returned. During testing, try submitting an order with multiple products, where one has insufficient inventory and another has sufficient inventory. You can see the returned response message: ![Example_Response Message after Submission](https://static-docs.nocobase.com/dd9e81084aa237bda0241d399ac19270.png) As you can see, the response message does not indicate that the first product, "iPhone 15 pro," is out of stock, but only that the second product, "iPhone 14 pro," is. This is because in the loop, the first product has sufficient inventory, so it is not intercepted, while the second product has insufficient inventory, which intercepts the order submission. ## External Invocation The Before Action Event itself is injected during the request processing phase, so it also supports being triggered via HTTP API calls. For workflows that are locally bound to an action button, you can call them like this (using the create button of the `posts` collection as an example): ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Hello, world!", "content": "This is a test post." }' "http://localhost:3000/api/posts:create?triggerWorkflows=workflowKey" ``` The URL parameter `triggerWorkflows` is the key of the workflow; multiple workflow keys are separated by commas. This key can be obtained by hovering the mouse over the workflow name at the top of the workflow canvas: ![Workflow_Key_View_Method](https://static-docs.nocobase.com/20240426135108.png) After the above call is made, the Before Action Event for the corresponding `posts` collection will be triggered. After the corresponding workflow is processed synchronously, the data will be created and returned normally. If the configured workflow reaches an "End node", the logic is the same as with an interface action: the request will be intercepted, and no data will be created. If the end node's status is configured as failed, the returned response status code will be `400`; if successful, it will be `200`. If a "Response message" node is also configured before the end node, the generated message will also be returned in the response result. The structure for an error is: ```json { "errors": [ { "message": "message from 'Response message' node" } ] } ``` The message structure when the "End node" is configured for success is: ```json { "messages": [ { "message": "message from 'Response message' node" } ] } ``` :::info{title=Note} Since multiple "Response message" nodes can be added in a workflow, the returned message data structure is an array. ::: If the Before Action Event is configured in global mode, you do not need to use the `triggerWorkflows` URL parameter to specify the corresponding workflow when calling the HTTP API. Simply calling the corresponding collection action will trigger it. ```bash curl -X POST -H 'Authorization: Bearer ' -H 'X-Role: ' -d \ '{ "title": "Hello, world!", "content": "This is a test post." }' "http://localhost:3000/api/posts:create" ``` :::info{title="Note"} When triggering a before action event via an HTTP API call, you also need to pay attention to the enabled status of the workflow and whether the collection configuration matches, otherwise the call may not succeed or may result in an error. ::: --- url: /workflow/triggers/schedule.md --- # Scheduled Task ## Introduction A scheduled task is an event triggered by a time condition, which comes in two modes: - Custom time: Regular cron-like triggering based on system time. - Collection time field: Triggering based on the value of a time field in a collection when the time is reached. When the system reaches the time point (accurate to the second) that meets the configured trigger conditions, the corresponding workflow will be triggered. ## Basic Usage ### Create a scheduled task When creating a workflow in the workflow list, select the "Scheduled Task" type: ![Create a scheduled task](https://static-docs.nocobase.com/e09b6c9065167875b2ca7de5f5a799a7.png) ### Custom time mode For the regular mode, you first need to configure the start time to any point in time (accurate to the second). The start time can be set to a future time or a past time. When set to a past time, it will check if the time is due based on the configured repeat condition. If no repeat condition is configured and the start time is in the past, the workflow will no longer be triggered. There are two ways to configure the repeat rule: - By interval: Triggers at a fixed interval after the start time, such as every hour, every 30 minutes, etc. - Advanced mode: That is, according to cron rules, it can be configured for a cycle that reaches a fixed rule-based date and time. After configuring the repeat rule, you can also configure an end condition. It can be ended at a fixed point in time or limited by the number of times it has been executed. ### Collection time field mode Using a collection's time field to determine the start time is a trigger mode that combines regular scheduled tasks with collection time fields. Using this mode can simplify nodes in some specific processes and is also more intuitive in terms of configuration. For example, to change the status of overdue unpaid orders to cancelled, you can simply configure a scheduled task in the collection time field mode, selecting the start time as 30 minutes after the order is created. ## Related Tips ### Scheduled tasks in an inactive or shutdown state If the configured time condition is met, but the entire NocoBase application service is in an inactive or shutdown state, the scheduled task that should have been triggered at that time point will be missed. Moreover, after the service is restarted, the missed tasks will not be triggered again. Therefore, when using it, you may need to consider handling for such situations or have fallback measures. ### Repeat count When the "by repeat count" end condition is configured, it calculates the total number of executions across all versions of the same workflow. For example, if a scheduled task has been executed 10 times in version 1, and the repeat count is also set to 10, the workflow will no longer be triggered. Even if copied to a new version, it will not be triggered unless the repeat count is changed to a number greater than 10. However, if it is copied as a new workflow, the execution count will be reset to 0. Without modifying the relevant configuration, the new workflow can be triggered another 10 times. ### Difference between interval and advanced mode in repeat rules The interval in the repeat rule is relative to the time of the last trigger (or the start time), while the advanced mode triggers at fixed points in time. For example, if it is configured to trigger every 30 minutes, and the last trigger was at 2021-09-01 12:01:23, then the next trigger time will be 2021-09-01 12:31:23. The advanced mode, i.e., cron mode, is configured to trigger at fixed time points, for example, it can be configured to trigger at 01 and 31 minutes of every hour. ## Example Suppose we need to check for orders that have not been paid for more than 30 minutes after creation every minute and automatically change their status to cancelled. We will implement this using both modes. ### Custom time mode Create a scheduled task-based workflow. In the trigger configuration, select "Custom time" mode, set the start time to any point not later than the current time, select "Every minute" for the repeat rule, and leave the end condition blank: ![Scheduled Task_Trigger Configuration_Custom Time Mode](https://static-docs.nocobase.com/71131e3f2034263f883062389b356cbd.png) Then, configure other nodes according to the process logic, calculate the time 30 minutes ago, and change the status of unpaid orders created before that time to cancelled: ![Scheduled Task_Trigger Configuration_Custom Time Mode](https://static-docs.nocobase.com/188bc5287ffa1fb24a4e7baa1de6eb29.png) After the workflow is enabled, it will be triggered once every minute from the start time, calculating the time 30 minutes ago to update the status of orders created before that time point to cancelled. ### Collection time field mode Create a scheduled task-based workflow. In the trigger configuration, select "Collection time field" mode, select the "Orders" collection, set the start time to 30 minutes after the order's creation time, and select "No repeat" for the repeat rule: ![Scheduled Task_Trigger Configuration_Collection Time Field Mode_Trigger](https://static-docs.nocobase.com/d40d5aef57f42799d31cc5882dd94246.png) Then, configure other nodes according to the process logic to update the status of the order with the trigger data ID and a status of "unpaid" to cancelled: ![Scheduled Task_Trigger Configuration_Collection Time Field Mode_Update Node](https://static-docs.nocobase.com/491dde9df8f773f5b14a4fd8ceac9d3e.png) Unlike the custom time mode, there is no need to calculate the time 30 minutes ago here, because the trigger data context contains the data row that meets the time condition, so you can directly update the status of the corresponding order. --- url: /workflow/triggers/webhook.md --- # Webhook ## Introduction The Webhook trigger provides a URL that can be called by third-party systems via HTTP requests. When a third-party event occurs, it sends an HTTP request to this URL to trigger the workflow execution. It is suitable for notifications initiated by external systems, such as payment callbacks, messages, etc. ## Creating a Workflow When creating a workflow, select the type "Webhook event": ![20241210105049](https://static-docs.nocobase.com/20241210105049.png) :::info{title="Note"} The difference between "synchronous" and "asynchronous" workflows is that a synchronous workflow waits for the workflow to complete before returning a response, while an asynchronous workflow immediately returns the response configured in the trigger and queues the execution in the background. ::: ## Trigger Configuration ![20241210105441](https://static-docs.nocobase.com/20241210105441.png) ### Webhook URL The URL for the Webhook trigger is automatically generated by the system and bound to this workflow. You can click the button on the right to copy it and paste it into the third-party system. Only the POST HTTP method is supported; other methods will return a `405` error. ### Security HTTP Basic Authentication is currently supported. You can enable this option and set a username and password. Include the username and password in the Webhook URL in the third-party system to implement security authentication for the Webhook (for details on the standard, see: [MDN: HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#basic_authentication_scheme)). When a username and password are set, the system will verify if the username and password in the request match. If they are not provided or do not match, a `401` error will be returned. ### Parse Request Data When a third party calls the Webhook, the data carried in the request needs to be parsed before it can be used in the workflow. After parsing, it becomes a trigger variable that can be referenced in subsequent nodes. The parsing of the HTTP request is divided into three parts: 1. Request Headers Request headers are usually simple key-value pairs of string type. The header fields you need to use can be configured directly, such as `Date`, `X-Request-Id`, etc. 2. Request Parameters Request parameters are the query parameters in the URL, such as the `query` parameter in `http://localhost:13000/api/webhook:trigger/1hfmkioou0d?query=1`. You can paste a full sample URL or just the query parameter part and click the parse button to automatically parse the key-value pairs. ![20241210111155](https://static-docs.nocobase.com/20241210111155.png) Automatic parsing will convert the parameter part of the URL into a JSON structure and generate paths like `query[0]`, `query[0].a` based on the parameter hierarchy. The path name can be manually modified if it doesn't meet your needs, but usually, no modification is required. The alias is the display name of the variable when used, which is optional. The parsing will also generate a full list of parameters from the sample; you can delete any parameters you don't need. 3. Request Body The request body is the Body part of the HTTP request. Currently, only request bodies with a `Content-Type` of `application/json` are supported. You can directly configure the paths to be parsed, or you can input a JSON sample and click the parse button for automatic parsing. ![20241210112529](https://static-docs.nocobase.com/20241210112529.png) Automatic parsing will convert the key-value pairs in the JSON structure into paths. For example, `{"a": 1, "b": {"c": 2}}` will generate paths like `a`, `b`, and `b.c`. The alias is the display name of the variable when used, which is optional. The parsing will also generate a full list of parameters from the sample; you can delete any parameters you don't need. ### Response Settings The configuration of the Webhook response differs between synchronous and asynchronous workflows. For asynchronous workflows, the response is configured directly in the trigger. Upon receiving a Webhook request, it immediately returns the configured response to the third-party system and then executes the workflow. For synchronous workflows, you need to add a response node within the flow to handle it according to business requirements (for details, see: [Response Node](#response-node)). Typically, the response for an asynchronously triggered Webhook event has a status code of `200` and a response body of `ok`. You can also customize the response status code, headers, and body as needed. ![20241210114312](https://static-docs.nocobase.com/20241210114312.png) ## Response Node Reference: [Response Node](../nodes/response.md) ## Example In a Webhook workflow, you can return different responses based on different business conditions, as shown in the figure below: ![20241210120655](https://static-docs.nocobase.com/20241210120655.png) Use a conditional branch node to determine if a certain business status is met. If it is, return a success response; otherwise, return a failure response.