diff --git a/docs/AI/agent-building-101.md b/docs/AI/agent-building-101.md
index fd787f141..b6df8f094 100644
--- a/docs/AI/agent-building-101.md
+++ b/docs/AI/agent-building-101.md
@@ -6,16 +6,16 @@ metadata:
description: kba_5v2bm
robots: index
---
-In Budibase, Agents enable AI to interact with the data, tools, and automations in your Workspace.
+In Budibase, Agents let AI interact with data, tools, and automations in your Workspace.
-In this guide, we’ll build a simple AI Service Desk Agent that can:
+In this guide, you will build a simple AI Service Desk Agent that can:
* Answer questions about tickets
* Categorise and summarise new tickets
* Update ticket statuses
* Escalate urgent issues automatically
-Along the way, we’ll learn how to:
+Along the way, you will learn how to:
* Configure an Agent’s instructions
* Connect Workspace data as tools
@@ -23,8 +23,6 @@ Along the way, we’ll learn how to:
* Trigger automations from Agent actions
* Test and iterate safely
-Let’s jump in.
-
## Before you start
Make sure you have:
@@ -49,15 +47,15 @@ Make sure you have:
### Setting up our data layer
-In Budibase, Agents can interact with any tables, API calls, or automations within your Workspace, as long as they’re explicitly given access.
+In Budibase, Agents can interact with any tables, API calls, or automations within your Workspace, as long as they are explicitly given access.
-Before creating an Agent, it’s important to understand the data it will work with and define what actions it should be allowed to perform.
+Before creating an Agent, define the data it will work with and the actions it should be allowed to perform.
-Take a look at our [Introduction to Data ](https://docs.budibase.com/docs/data) documentation to learn more about how data works in Budibase.
+See [Introduction to Data](https://docs.budibase.com/docs/data) for more detail on how data works in Budibase.
#### Our tickets table
-For this guide, we’ll use a Budibase DB table called Tickets.
+For this guide, use a Budibase DB table called Tickets.
This contains the following columns:
@@ -74,7 +72,7 @@ This contains the following columns:
### Creating our agent
-Onto creating our first Agent. Make sure you have done the initial setup of selecting a provider and configuring a model before doing this. You can find out how to do this [here](doc:agents-config).
+To create your first Agent, make sure you have selected a provider and configured a model first. See [Agents config](doc:agents-config).
1. Click on the **Agents** link on the left side of the screen
2. Click the **+ New Agent** button at the top right of your screen
@@ -91,7 +89,7 @@ Agents perform tasks through **Operations**. An Agent can have one or many opera
By default, Budibase provides a structured instruction template to help you define your operation clearly. More information on this can be found [here](doc:agent-instructions-guide#recommended-template).
-For now we will use the below instructions:
+Use the following instructions for this guide:
```markdown instructions
**Operation role**
@@ -123,7 +121,7 @@ When categorising or prioritising, return structured JSON:
- Use British English where possible
```
-After configuring and adding the above instructions, run a test and confirm that everything is working.
+After adding the instructions, run a test and confirm that everything works.
### Adding tools
@@ -147,7 +145,7 @@ Together, these allow the Agent to:
* Retrieve specific ticket details
* Update ticket status, priority, or resolution notes when required
-> As a best practice, only enable the minimum set of tools required. Limiting tool access helps ensure predictable and safe behaviour.
+> As a best practice, only enable the minimum set of tools required. Limiting tool access helps keep behaviour predictable and safe.
### Adding Knowledge
@@ -172,7 +170,7 @@ For more complex configurations, you can use the **Advanced setup** option withi
Once these tools and knowledge sources are enabled, we need to guide the Agent on when to use them.
-Here is our updated instruction prompt:
+Use the following updated instruction prompt:
```markdown instructions
**Operation role**
@@ -226,7 +224,7 @@ You do not need to manually configure this routing; it is handled automatically
### Testing the agent
-Now that our Agent has instructions and tools, we can test whether it behaves safely and predictably.
+Now that your Agent has instructions and tools, you can test whether it behaves safely and predictably.
Start with a few representative prompts:
diff --git a/docs/Apps/actions/application-actions.md b/docs/Apps/actions/application-actions.md
index 671405d6e..6029f525d 100644
--- a/docs/Apps/actions/application-actions.md
+++ b/docs/Apps/actions/application-actions.md
@@ -1,8 +1,6 @@
---
title: Application actions
-excerpt: >-
- These actions can be anything from notifications, to triggering automations or
- navigating between screens.
+excerpt: Actions for navigation, notifications, modals, and automations.
deprecated: false
hidden: false
metadata:
@@ -16,69 +14,62 @@ next:
slug: form-actions
title: Form actions
---
-## Navigate To
+Use application actions when a user interaction should change screens, open UI, show feedback, or trigger automation.
-Use this action if you want to go to another web page or app screen.\
-For example: [Navigation on row button click](doc:url-parameters)
+## Navigate To
-When the *Destination* is set to 'URL', you have the option to open the link in a new tab.
+Use this action to open a URL or another screen.
-
+If the destination is a URL, you can open it in a new tab.
-Alternatively, if the *Destination* is set to 'Screen', then you can select a screen to navigate to from the dropdown. The screen can optionally be opened in the current tab, or in a [modal](https://docs.budibase.com/docs/screens#modal-screen).
+If the destination is a screen, you can open it in the current tab or in a modal.
You can learn more about using URL parameters with the `Navigate To` action [here](https://docs.budibase.com/docs/url-parameters).
-
-
-## Trigger Automations
+## Trigger automations
-Often there are times where you would like to trigger a automation after a user has performed an action. There are a range of different [Automations](doc:custom-automations). You are able reference any created automations by clicking the `Use an existing automation` radio button and then selecting an existing previously created automations to use and run.
+Use this action to run an existing automation.
-
+Pass values into the automation with fields on the action.
-Lastly, creating fields within the automation itself allows data/bindings to be sent to it, this can then be used in your automation to perform whatever task you require. An example of this would be passing an `_id` of a row to the automation to then update the row's state e.g. from `Pending` to `In progress`.
-
-
+This is useful when a user action should kick off a background process.
## Logout
-If you want to log a user out, you can use this action. It has redirect functionality built in meaning you can redirect to whatever url you desire.
-
-## Close Screen Modal
+Use this action to sign the current user out and optionally redirect them.
-If the current screen has been opened as a [Modal](https://docs.budibase.com/docs/screens#modal-screen), then close it. Additionally, you can navigate to a new screen or open a different screen modal afterwords also using this action.
+## Close screen modal
-## Close Modal
+Use this action to close the current screen modal.
-This action takes no settings, and will close any modal component open on screen.
+You can also navigate to another screen after closing.
-## Open Modal
+## Close modal
-Opens a modal component on a screen - different to a "Navigate to" action with "Open screen in modal", which opens an entire screen in a modal, this action opens a modal component that is already on the current screen. See [modal component]()
+Use this action to close the currently open modal component.
-## Show Notification
+## Open modal
-This allows you to show a popup notification, of which there are four different types; `Success`, `Warning`, `Error` and `Info`. Changing the type will change the styling of the popup for example: success will be have a green background where as error will have a red background. Additionally you can make use of the bindings to output more informative messages. E.g. `Hi {{ Current User.email }} welcome to Budibase.`
+Use this action to open a modal component that already exists on the current screen.
-
+## Show notification
-### Logic within notifications
+Use this action to show a success, warning, error, or info message.
-It is possible to use some logic with the notification - if the result of the logic running is falsy, no message will show. This can be used in conjunction with a "Continue if/Stop If" step to show the user some feedback explaining why the action has stopped.
+You can include bindings in the message text.
-## Prompt User
+It is also possible to use some logic with the notification - if the result of the logic running is falsy, no message will show. This can be used in conjunction with a "Continue if/Stop If" step to show the user some feedback explaining why the action has stopped.
-This action adds an additional step before other actions are run. The title, message, and button text can all be customised. It provides a prompt to the user, and won't run any other action after it unless they click the confirm button. This is very useful for a longer chain of actions, as it provides context and gives the option to cancel the process before it starts
+## Prompt user
-
+Use this action to confirm a user decision before continuing a chain of actions.
-
+If the user cancels, later actions do not run.
-## Open Side Panel
+## Open side panel
-Provides a list of all side panels within a current screen, selecting one will open that side panel whenever the action is hit.
+Use this action to open one of the screen's side panels.
-## Close Side Panel
+## Close side panel
-Closes the currently open side panel.
+Use this action to close the currently open side panel.
diff --git a/docs/Apps/actions/chaining-actions.md b/docs/Apps/actions/chaining-actions.md
index bdc1fb278..b990b2aed 100644
--- a/docs/Apps/actions/chaining-actions.md
+++ b/docs/Apps/actions/chaining-actions.md
@@ -10,195 +10,44 @@ metadata:
next:
description: ''
---
-Using Budibase, you can create a sequence of actions. Actions run synchronously; if one fails, the actions after won't run.
+Use chained actions when later actions depend on the result of earlier ones.
-
+Actions run in order. If one fails, later actions do not run.
-## Action referencing
+## Access previous results
-Accessing data from a previous action in a new action can be useful.
+Some actions expose their output to later actions.
-The following actions expose data after execution:
+Common examples include:
-
- `{ data: [ _ ] }`
-
- * An object as determined by the query schema
-
-
-
-
-
- Trigger Automation
-
-
-
- \{\{ Action X.Automation Result }}
-
-
-
- The response of a [Synchronous automation 🔒](doc:synchronous-automations)
-
-
-
-
-
-**Note** X is the action number
-
-
-
-***
+Use the action result binding from the previous step to read that output.
## Query result properties
-In addition to the payload data of an execute query action result, you can also access the following additional information:
-
-* Status code - *Number*
-* Response time - *String including the time unit*
-* Payload size - *String including the size unit*
-
-This can be useful in combination with [Conditional UI](doc:conditions) for navigating to different screens upon an error, or to change text color based on the status code.
-
-### Example
-
-1. Save the query result to [App state](doc:app-state). Use the **literal** keyword to ensure an object is saved, and not text.
-
-
-
-2. Reference the metadata properties with the following Bindings:
-
-```handlebars
-### Status code: {{ State.QueryResult.code }}
-
-### Response time: {{ State.QueryResult.time }}
-
-### Payload size: {{ State.QueryResult.size }}
-```
-
-
- Response info returned after executing the query
-
-
-
-
-***
-
-## Tutorial: Creating and Linking two table entries in one form
-
-#### Schema
-
-* Bookings
-* Leaders
-* One Booking -> One Leader
-
-***
-
-#### Use Case
-
- Create a new booking and a new associated leader in one form
-
-***
-
-#### Steps
-
-1. In the Data tab, using the internal BudibaseDB, create a new table, "guides", containing the following columns - name (text), phone\_number(text), email(text). Populate these with a few rows of dummy-data.
-2. Create another table, "tours", containing the following columns - destination(text) date(date, set to "Date only").
-3. In the tours table, add a column called "guide", with a relationship of many rows in tours to one row in guides. Set the column name in the other table to be "tours".
-
-
-4. Switch back to your guides table to see that the relationship column has been created. The circle with an arrow denote that this column is a relationship to another table.
-
-
-5. Create a new app called "Creating and Linking two table entries in one form"
-6. Add a new screen, add a form component (not to be confused with a Form Block). Set the form component to create a row in the tours table.
-
-
-7. Add a Text Field component to your form, and set the name as "destination" and the label as "Tour Destination"
+Execute query actions also expose:
-
-8. Repeat the previous step until your form has all of the following
+* Status code - `{{ Action 1.Query result.code }}`
+* Response time - `{{ Action 1.Query result.time }}`
+* Payload size - `{{ Action 1.Query result.size }}`
-| Field | Name | Label |
-| :--------- | :---------- | :------------------------- |
-| Text Field | destination | Tour Destination |
-| Date Field | date | Tour Date |
-| Text Field | TGname | Tour Guide's Name |
-| Text Field | TGphone | Tour Guide's Phone Number |
-| Text Field | TGemail | Tour Guide's Email Address |
+You can use these values in conditions or display them in the UI.
-8. Next, add a button as a child of your Form Component - Set the text to "Save" and (optional) add an Icon
-9. Add an On click action to the button: Validate Form will catch any "required" fields, as well as any custom validation set on a per-field basis (though not applicable in this guide).
-10. Add another action - Save Row. Select your form as the datasource, and set the table as tours. Add two columns from the tours table, destination and date, and assign the values from the destination and date fields accordingly.
+## Common pattern
-
+Use chained actions when you need to:
-
-11. Add another Save Row action, but this time we will save the Tour Guide's information to the guides table. Look closely at the fourth column - tours - this is saving row\_id from the previous action, which is linking the two rows together. This action will save a new tour and a new guide, and show the relationship between them.
+* Validate a form
+* Save a row
+* Reuse the saved row ID in another action
+* Clear the form or navigate after the save completes
-
-12. (optional) Check the "Do not display default notification" on *one* of the Save Row actions.
-13. Add a "Clear Form" action to reset the form back to it's default values
+## Related pages
-You could also use a "Navigate To" action to take the user to a screen showing the information in a table, or back to the homepage.
\ No newline at end of file
+* [Form actions](doc:form-actions)
+* [Data actions](doc:data-actions)
+* [Conditional UI](doc:conditions)
diff --git a/docs/Apps/actions/data-actions.md b/docs/Apps/actions/data-actions.md
index ffbd701a6..a631b95bf 100644
--- a/docs/Apps/actions/data-actions.md
+++ b/docs/Apps/actions/data-actions.md
@@ -1,6 +1,6 @@
---
title: Data actions
-excerpt: These are data based actions; CRUD and other data manipulations.
+excerpt: Actions for saving, updating, exporting, and fetching data.
deprecated: false
hidden: false
metadata:
@@ -14,153 +14,68 @@ next:
slug: application-actions
title: Application actions
---
-## Save Row
+Use data actions when user input should create, update, delete, export, or refresh data.
-Saving a row can mean two things: updating or adding a new row. Depending on what you want to do you will have to structure this action differently.
+## Save row
-If you're adding, updating or viewing any row the easiest way is to add a [Form Block](doc:form-block) component. Here you can choose to create, update or read, depending on your selection you will be prompted with additional fields. For update and read you will have to pass in the `Row ID` into the row field, this is what will be use to update the specific record back to your chosen data source. In this instance you don't have to worry about the `Save Row` action as it already preconfigured for you. (You can [eject blocks](https://docs.budibase.com/docs/blocks#ejecting-blocks) if you want to see a good example of how the `Save row` action is configured). However, if you did go down the regular [Form](doc:form) route your save row action would look something to the below.
+Use this action to create or update a row.
-
+If you are using a form block, the save action is already configured for you.
-Updating a row can be done in a number of different ways. If you have a structure containing a [Data provider](https://docs.budibase.com/docs/data-provider) and a [Repeater](https://docs.budibase.com/docs/repeater), selecting the repeater as your Datasource will make it so you can correctly get the values from the specific row. To change a field you then take advantage of the add column button that is at the bottom. Click it, select the field you want to change, and type in the value you want to change it to (or use a binding). A descriptive image of this is shown below:
+For custom forms, add the columns you want to update and bind the values into the action.
-
+When updating a row directly, provide the row `_id`.
-If you are not using a repeater, an alternative way to update a row is to add the **\_id** column and provide a value that matches an existing id.\
-For example, you could be passing a task id in the URL:
+## Duplicate row
-
+Use this action to copy a row into another table with matching columns.
-## Duplicate Row
+If the action is triggered from a row button, the row context can supply the source row automatically.
-This action allows you to clone a row into any table with shared column names.\
-If you are performing this action from a row button click, then no **\_id** needs to be provided.
+## Delete row
-Alternatively, your button could be nested within a [Repeater](https://docs.budibase.com/docs/repeater). The below example shows how this would be setup with a repeater.
-
-
-
-## Delete Row
-
-This action is used to delete rows from your data source. It's used much in the same way that you would when updating a row. The main difference being you don't have to select a data source. You only have to select the table and pass the correct `Row ID` binding. For this example we will pass the `Row ID` via a [URL variable](doc:url-parameters), then the `Row ID` will be passed into the delete on click action used to delete the record from the [Budibase DB](doc:budibasedb).
-
-
+Use this action to delete a row by table and row ID.
## Fetch row
-This action allows you to fetch a table row from any table within your app. This action works the same `Delete Row` where you have select a table and reference the `Row ID`.
-
-
-
-This can be used alongside other actions a good example of this is with [App state](doc:app-state). You can fetch the row then create a state to hold all the data related to the row fetched. This can then be used to as the basis to bind to different components within your app.
-
-Below there is a simple demo showing this in action. This is a very simple demo and its only outputting the entire fetch object to a paragraph component. You can break this up into multiple components and output different parts of the object if you so choose.
-
-
-
-## Execute Query
-
-The `Execute Query` action requires separate configuration as the queries need to be setup before they can be used by this action within your apps. There are many ways to use this action. For instances, you can use it to send the query results to a [REST API](doc:rest), retrieve results from a query created using an [External data source](doc:data-sources), and so on.
-
-In the example below we will be focusing on using this with a REST datasource. This example won't be too in-depth and shall only show a basic get REST query. This will also be used in a similar fashion to `Fetch rows` action, where we will storing it as state to display the object.
-
-
-
-For anything more advance have a read through the [REST API](doc:rest) docs. It will show you how to import and configure everything necessary to perform a more advanced action.
-
-
-
-## Refresh Data Provider
-
-This is mainly used for [External datasources](doc:data-sources) for example the [REST API](doc:rest) as it requires the recalling of queries in order to refresh [Data providers](doc:data-provider) after other actions have been performed. A good example for this is when you update a user record within a [Form](doc:form) inside a [Side panel](doc:side-panel). Once updated you would want to chain the `Refresh Data Provider` action afterwards to refresh the changes made without having to refresh the page to see these changes. Another example of this might be for the [Candlestick Chart](https://docs.budibase.com/docs/candlestick-chart) where new data is available over time, and it may be useful to refresh at the click of a button.
-
-## Update State
-
-Allows you to set a state value, or delete the key entirely. When entering an identifying *Key* you can select an existing one from the dropdown, or simply manually type into the text box to create a new key.
-
-
-
-Your state variable can then be accessed, anywhere in your app, via the state Binding, in this case: `{{ State.employee_id }}`.
-
-See [App state](doc:app-state) for further reading and examples.
-
-## Upload File To S3
-
-To be used with the [S3 File Upload](doc:s3-file-upload) component.
-
-## Export Data
-
-This action is used to export selected rows within a table to either a CSV or JSON file. To set this up quickly add a [Table](https://docs.budibase.com/docs/table) component to a screen, and set it to display the Employees table from the sample BudibaseDB data. Add a button component underneath, and add an On Click action "Export Data".
-
-
-
-Once this has been done, it should export the rows selected and output it as the selected export.
-
-
-
-
-
-#### Configure delimiter
-
-The default delimiter (separator) for CSV exports is a comma `,` . It is however possible to change this to one of a preset list of options. This feature does not apply to JSON file exports.
-
-
- Using the tab key delimiter
-
-
-#### Configure export columns
-
-By default *all* table columns will be included in the export, however you can use a subset and provide custom labels.
-
-Click the *Export columns* button and add the specific columns you want to export. You can also drag-and-drop the columns to rearrange the column order in the export.
+Use this action to read a single row and pass it into state or another component.
-
- Only include these three columns in the data export
-
+## Execute query
-
- Exported CSV file
-
+Use this action to run a saved query.
-## Clear Row Selection
+This is common for REST queries and external datasources.
-Deselects any selected rows of the chosen table.
+## Refresh data provider
-
+Use this action to reload a data provider after another action changes data.
-
+## Update state
-Can also be useful in combination with the **Open Side Panel** action.
+Use this action to set or clear an app state value.
-## Download File
+## Upload file to S3
-Initiates the download of a specified file or files.
+Use this action with the [S3 File Upload](doc:s3-file-upload) component.
-### Download from Attachment
+## Export data
-
+Use this action to export selected table rows as CSV or JSON.
-Specify the table and column from which the file(s) will be downloaded - only columns that have the type `Single Attachment` or `Multi Attachment` will appear in the `Column` dropdown.
+You can choose the delimiter for CSV exports and limit the exported columns.
-Downloading from Multi Attachment columns will result in a `.zip` file containing all items being downloaded with a filename comprising of the name of the [Display Column](https://docs.budibase.com/docs/budibasedb#selecting-the-display-column) followed by the name of the Multi Attachment Column.
+## Clear row selection
-### Download from URL
+Use this action to deselect any currently selected table rows.
-
+## Download file
-Specify the URL of the file to be downloaded. If `File name` is left blank the file downloaded will be named "undefined" and appended with the file extension.
+Use this action to download a file from an attachment column or a URL.
## Row action
-With V3, a new automation called [Row Actions](doc:row-actions) was introduced. This allows for a data action that triggers specific automations. The Row Actions action includes four configurable settings:
+Use this action to trigger a row action automation.
-1. **Table or View**
- 1. Specify the table or view where the row action has been created
-2. **Row ID**
- 1. Provide the Row ID that the row action will use.
-3. **Row Action**
- 1. Select the specific row action you want to trigger
-4. **Require Confirmation**
- 1. This is a checkbox option. A modal will appear when selected, requiring user confirmation to trigger the row action automation. Additional fields will be displayed, enabling you to customize elements of the modal, such as the title, body text, and button labels.
+You must provide the table or view, row ID, and row action to run.
-
\ No newline at end of file
+Enable confirmation if users should confirm before the automation starts.
diff --git a/docs/Apps/actions/form-actions.md b/docs/Apps/actions/form-actions.md
index 41e1470f6..c8b36c4af 100644
--- a/docs/Apps/actions/form-actions.md
+++ b/docs/Apps/actions/form-actions.md
@@ -1,6 +1,6 @@
---
title: Form actions
-excerpt: These actions specifically deal with form interaction.
+excerpt: Actions for working with form values and steps.
deprecated: false
hidden: false
metadata:
@@ -14,60 +14,47 @@ next:
slug: logic-actions
title: Logic actions
---
-## Update Field Value
+Use form actions when an action needs to change a form field, move between steps, or validate a form.
-This action will update the value of a form field with a literal value or Binding.
+## Update field value
-The *Reset to default value* type will change the value of the chosen field to its Default Value.
+Use this action to set a form field to a literal value or binding.
-
+You can also reset a field to its default value.
-## Scroll To Field
+## Scroll to field
-This action lets you scroll to any form field on the page. Attach it to components with on change, on click, or even on load buttons throughout your application.
+Use this action to scroll a form field into view.
-To set it up, select the form containing your item and choose the field to scroll to when the action occurs. In the example below, a form component is linked to a button click event, causing it to scroll to the specified form field.
+This is useful for validation errors or long forms.
-
+## Validate form
-## Validate Form
+Use this action to validate a selected form.
-Allows you to validate any form that you've selected from a list of available forms.
+If validation fails, the form shows the relevant errors.
-
+## Change form step
-More on [Form validation](https://docs.budibase.com/docs/form-validation).
+Use this action with multi-step forms to move between steps.
-## Change Form Step
+Available step targets include:
-This along with the [Form step](doc:form-steps#design) component can allow you to break up larger forms into steps, it provides you with the option to enhance a users experience if you so choose.
+* Next step
+* Previous step
+* First step
+* Specific step
-This action lets you select the form you wish to change the current step of. It provides a few different options.
+## Clear form
-
+Use this action to clear the values from a selected form.
-1. `Next step` this simply increments the step of the form by 1.
-2. `Previous step` this will decrease the form step by 1.
-3. `First step` this sets the form step to the initial step.
-4. `Specific step` this adds a new option when selected, which allows you to type in a number that you want the step changed to.\
- 
-
-## Clear Form
-
-This clears whatever form's field values you've assigned to it, used here in conjunction with a "Prompt User" action.
-
-
-
-To view this quickly you can add a [Form block](doc:form-block) component eject the block and click the button component contained within. Afterwards, click the `Define actions` button on the far right of the screen within the settings area. From here you can see a list of pre generated actions one being the clear form action, clicking into this you will be able to see how its setup. Its extremely simple all thats required is selecting the form you wish to perform this action on.
-
-
+This is commonly used after a submit or confirmation prompt.
## On change
-Form fields have the ability to trigger an action *on change* of their value.
-
-Specifically, [Pickers](doc:pickers) will update on selection, whereas [Text input](https://docs.budibase.com/docs/text-inputs) fields will update on loss of focus.
+Form fields can trigger actions when their value changes.
-
+Picker fields update when the selection changes. Text inputs update when focus leaves the field.
-In addition, there is a convenient Binding `{{ Field Value }}` which represents the value of the trigger form field.
\ No newline at end of file
+Use the `{{ Field Value }}` binding to read the value that triggered the action.
diff --git a/docs/Apps/actions/index.md b/docs/Apps/actions/index.md
index b37ca8714..74f75ad89 100644
--- a/docs/Apps/actions/index.md
+++ b/docs/Apps/actions/index.md
@@ -10,30 +10,11 @@ metadata:
next:
description: ''
---
-There are a wide variety event actions throughout Budibase. These can be split into four different types: Data, Application, Form and Logic actions.
+Budibase actions are grouped into data, application, form, and logic actions.
-## Data actions
+## Action groups
-As the title implies, these actions revolve around data. They involve tasks like retrieving data from different sources (like REST APIs or external databases), refreshing data sources, or making changes to data (such as updating rows in a database). You can view this [here.](doc:data-actions)
-
-## Application actions
-
-These are actions that can happen at any time in your application. They include things like displaying notifications, opening side panels or pop-up windows, logging out users, and even activating automated processes. This can be found [here.](doc:application-actions)
-
-## Form actions
-
-There are fewer actions of this kind compared to data and application actions. When it comes to form actions, there are four different actions: `Update Field Value,` `Validate Form,` `Change Form Step,` and `Clear Form.` These actions specifically deal with manipulating different parts of a form and can be found in more detail [here.](doc:form-actions).
-
-## Logic actions
-
-Currently there is only one action of this type, this is the [Continue If / Stop if](https://docs.budibase.com/docs/chaining-actions#continue-if--stop-if).
-
-![]()
-
-***
-
-## Tutorial video
-
-{`
-
-`}
+* [Data actions](doc:data-actions)
+* [Application actions](doc:application-actions)
+* [Form actions](doc:form-actions)
+* [Logic actions](doc:logic-actions)
diff --git a/docs/Apps/app-scripts.md b/docs/Apps/app-scripts.md
index 3dcbdfbe3..7d40872de 100644
--- a/docs/Apps/app-scripts.md
+++ b/docs/Apps/app-scripts.md
@@ -14,109 +14,44 @@ next:
slug: custom-css
title: Custom CSS
---
-These scripts are injected into your app's server-side rendered HTML skeleton and take effect immediately. This allows them to override or manipulate any part of your app.
+Use app scripts to add custom scripts, styles, and external references to your app.
-## Unlocking app scripts
+These scripts are injected into the app shell and apply across the app.
-You must upgrade to an enterprise plan to unlock app script functionality. For more information, contact our [sales team here](https://budibase.com/contact/).
+## Unlock app scripts
-## Setup
+App scripts require an enterprise plan.
-You can configure app scripts in your app settings under a page titled "App Scripts."
+## Add a script
-
+1. Open app settings
+2. Go to `App Scripts`
+3. Select `Add script`
+4. Enter a name
+5. Choose whether the script loads in the head or body
+6. Add the HTML, script, or style content
+7. Save the script
-You can add a new script by clicking the "Add script" button in the top right of the table. The configuration of each script is broken down into 3 key elements;
+## Script content
-* The Name field is a descriptive title of what your script does
-* The Location field is a options picker that will control whether this script is added to the head of the page or the body
-* The HTML field is where you can add the content to the head or body of your app. You can include:
- * JavaScript scripts
- * CSS styles
- * External style sheets
- * Additional JavaScript references
+You can add:
-You can save your app script configurations once you've filled out the fields.
+* JavaScript
+* CSS
+* External stylesheets
+* External JavaScript references
-To view your saved scripts, return to the "App Scripts" page. Click a row to open the form with pre-filled information. You can then save, cancel, or delete the script as needed.
+## Use CSP whitelist domains
-
+Use the CSP whitelist when your scripts need to fetch resources from other domains at runtime.
-## Domain Whitelisting for Content Security Policy (CSP)
+You do not need to whitelist domains for scripts that are already loaded through App Scripts.
-You can now specify external domains to be added to your app's [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP), enabling safe access to external resources or APIs.
+## Common use cases
-This configuration is available under the "App Scripts" page in a new section titled "CSP Whitelist Domains."
+App scripts are commonly used for:
-### Why is this needed?
-
-Browsers use CSP to control which resources a page can load. By default, only safe resources from your app’s domain are allowed. If your scripts fetch data or load additional resources from external domains, CSP may block them unless those domains are explicitly allowed.
-
-### When do I need to use this?
-
-You **only** need to add a domain to the CSP whitelist if:
-
-* A script or style you've added fetches additional resources (e.g., images, styles, JS) from other domains at runtime
-* Your script makes AJAX/fetch requests to a different domain (e.g., calling an external API)
-
-You **do not need** to whitelist a domain if:
-
-* You're just loading an external script via the App Scripts feature. These scripts are automatically secured with a nonce.
-* You're using self-hosted Budibase with CSP disabled. In that case, all domains are already allowed.
-
-### How to add domains
-
-On the “App Scripts” page:
-
-1. Scroll to the CSP whitelist section
-
-
-2. Add one or more domains (e.g., [https://api.example.com](https://api.example.com))
-3. Save your changes
-
-Domains added here will be included in the default-src directive of your app’s CSP, allowing access to most types of external resources.
-
-## Examples
-
-### Changing font to inter
-
-* Add the relevant style sheets
-* Then override the font with the below inline styling
-
-```Text HTML
-
-
-
-
-
-```
-
-
-
-### Adding a chatbot
-
-This requires two scripts,
-
-* A script in the head that will link to the chatbot's JS file.
-
- ```Text HTML
-
- ```
-
-
-* A script in the body that will add the following web component in the body to render the chatbot.
-
- ```Text HTML
-
- ```
-
-
-
-#### Result
-
-
+* Global styling overrides
+* External libraries
+* Embedded widgets
+* Runtime configuration
diff --git a/docs/Apps/app-state/index.md b/docs/Apps/app-state/index.md
index 1048de67a..51dc705b7 100644
--- a/docs/Apps/app-state/index.md
+++ b/docs/Apps/app-state/index.md
@@ -14,77 +14,37 @@ next:
slug: searchfilter-data
title: Adding filtering
---
-App state is a generic store of values that can be used to easily pass values around and persist them between navigation and page reloading.
+App state stores values that can be read and updated anywhere in your app.
-Unlike other Binding values, App state values can be set and used anywhere in your application. To set an app state value, you can use an action called `Update state` in the actions drawer. There are two types: Setting and Deleting. Set a key as the unique identifier; this is used when referencing the state value elsewhere in your app. You can select an already existing one or create one by typing in a name. After this, you can enter any value into the value input field. In addition to static values, you can also use Binding values here.
+Use it when you need to carry data between components, screens, or navigation events.
-
+## Set a value
-## App state tutorial
+Use the `Update state` action to create or change a state value.
-The example below shows a simple way to use app state within a Budibase app. The overview includes creating a select input that will save the field value to state. This state value will then be used to show and hide components based on the selected value.
+Each state entry has:
-1. Add a [Form](doc:form) component\
- 
-2. Add a [Options picker](doc:options-picker) component, then change the `Options source` from schema to custom.
-3. Click the `Define Options` button and add your options for this demo the options used are: Tables, Blocks and Images.\
- 
-4. After this click the `On change` button, search for the `Update state` action. Add this and then configure your state settings to something similar to the below.\
- 
-5. After this build out your page however you want, add additional options to your select input if you choose to.
-6. Setup conditionals for the various components throughout using the `Configure conditions` button within the conditions tab.
+* A key, which is the name you reference elsewhere
+* A value, which can be static or bound to another source
-
+## Use app state
-9. Repeat step 8 for the other options you want to apply conditions to
+Once a value exists, reference it through the `State` binding namespace.
-**Short Demo**
+Common uses include:
-
+* Passing the selected row into a form or side panel
+* Storing a temporary selection while a user moves through a multi-step form
+* Showing or hiding components based on a user choice
-### App state screen generation
+## Persist a value
-For more context on app state, some components already include this feature. A good example is the [Auto generate screens](doc:screens#autogenerated-screen) feature, where you can quickly see app state in action by generating screens based on preexisting data. This feature is particularly useful for generating screens with side panels or forms. Once generated, you can view these side panels and forms, and observe how state is applied to the edit form types. Additionally, if you navigate to the [Table](doc:table) component and click the `ON ROW CLICK` button, you will see some pre-generated actions that demonstrate state handling.
+Enable persistence when the value should survive reloads. Budibase stores persisted state in the user's browser.
-
+## Delete a value
-What this is doing is getting the clicked rows `_id` and adding it to a [Form Block](doc:form-block) which is automatically set to update. This newly created state key is then added into the `Details form block Row ID` field which is what is use to save any updates made back to that specific row.
+Delete a state value when it is no longer needed. This removes the stored value and clears any persisted copy.
-
+## Related guide
-### App state examples
-
-There are a range of different ways app state can be used within Budibase. A few examples can be seen below.
-
-* **Multiple step forms**, you can use App state to save the `_id` of the row which you are currently creating. App state can then be used to both save your place in the form and to save information at different stages back to your database as you process through your multiple step form.
-* **[Form Block](doc:form-block)** As talked in the **App state demo** you can use app state to pass specific rows `_id` into forms. Whether this be a form within a [Side Panel](doc:side-panel) component or a form on a different page, app state can be used in both cases.
-
-### App state Github discussions
-
-* [Bindings & app state](https://github.com/Budibase/budibase/discussions/10094) logging active persisted app states.
-* [Collapsible panel + conditional UI](https://github.com/Budibase/budibase/discussions/5763) Slightly more complicated use of state with conditional UI.
-* [Table Filter with a button](https://github.com/Budibase/budibase/discussions/10402) Trigging search with a button click instead of on change using state.
-
-***
-
-## Persisting values
-
-If you wish for the values that the user has selected to persist you can select the Persist this value checkbox beneath the key/value fields. Doing so will save the value in the users browser using the localStorage API. (A gif below has been attached to showcase the difference between persisted state and regular state.)
-
-
-
-***
-
-## Deleting values
-
-Deleting a value will delete it permanently and will also override values that are persisted. (Below is an example of deleting state.)
-
-
-
-***
-
-## Referencing state values
-
-Once you have created a value you are free to bind to it across your application. All values live under the **State** top-level Binding value and are visible in the bindings drawer.
-
-For example, To get the value in the 1st image you would enter `{{ State.isPersonal }}`. The pre generated example image using the [Table Block](doc:table-block) component you'd have to enter `{{ state.ID_bI8jAVBgC }}`.
+* [State explorer interface](doc:state-explorer-interface)
diff --git a/docs/Apps/app-state/state-explorer-interface.md b/docs/Apps/app-state/state-explorer-interface.md
index fa09995de..d989a1a4a 100644
--- a/docs/Apps/app-state/state-explorer-interface.md
+++ b/docs/Apps/app-state/state-explorer-interface.md
@@ -13,58 +13,25 @@ metadata:
next:
description: ''
---
-## Overview
+The State Explorer shows where a state value is set and where it is used on the current screen.
-The State Explorer Interface provides users with greater visibility and control over state variables used within a screen. It enables users to see:
+Use it when you need to trace a state value through a screen without hunting through components manually.
-* Which state variables are in use
-* Where they are being used
-* Which components they are attached to
+## Open the State Explorer
-Previously, there was no way to track which state variables were used or which components they were associated with. This feature addresses that gap, improving debugging and interface management.
+1. Open the **State** tab beside **Components** and **Bindings**
+2. Select a state variable from the dropdown
+3. Enter a temporary value if you want to preview the screen with sample data
-## Accessing the State Explorer
+## Read the results
-To open the State Explorer Interface:
+The State Explorer separates usage into two groups:
-1. Navigate to the State tab to the right of Components and Bindings.
-2. Click on the tab to reveal the State Variables selection field.
-3. The first state variable in the list is selected by default.
-4. To view a different state variable, select it from the dropdown list.
+* **Controlled by** lists components that update the state value
+* **Updates** lists components that read the state value
-
-5. Below the **State variables** selection field, you will see the **Set Temporary Value for Design Preview field**.
+Use these lists to find the exact component or binding that needs to change.
-### Set Temporary Value for Design Preview
+## Jump to a component
-The Set Temporary Value for Design Preview field allows users to input a temporary value for preview purposes. This field can either be manually typed into or set using available internal Budibase bindings, providing flexibility when testing or designing interfaces.
-
-
-
-## Understanding State Usage
-
-When a state variable is selected, the State Explorer displays relevant information about its usage within the current screen. This information is categorised into two sections:
-
-### Controlled By
-
-This category lists state variables that are set/modified. These include:
-
-* Input fields updating state
-* Button on clicks that trigger state changes
-* Other interactive elements affecting state values
-
-
-
-### Updates
-
-This section shows where the state variables are being used. Examples include:
-
-* A Paragraph component displaying the state value
-* A Form Block applying the state variable to the RowID setting
-* Data provider and or table filters
-
-
-
-## Navigating to Components
-
-For each entry in the Controlled By and Updates sections, the associated component is listed as a clickable item. Clicking on a component will navigate directly to its Settings or Binding area, allowing users to see and modify how the state variable is used.
\ No newline at end of file
+Click any entry in the list to open the related component settings or binding. This is the fastest way to update a state variable that is used in multiple places.
diff --git a/docs/Apps/blocks/form-block.md b/docs/Apps/blocks/form-block.md
index cb393a895..6d91ccf8e 100644
--- a/docs/Apps/blocks/form-block.md
+++ b/docs/Apps/blocks/form-block.md
@@ -10,121 +10,37 @@ metadata:
next:
description: ''
---
-Form blocks combine all the necessary components to build a comprehensive and flexible form.
+Use the Form block to create a form quickly from a table, view, or relationship schema.
-There are three key types.
+## Types
-| Type | Fields | Save button |
-| :----- | :------------------------------------------------------------------------- | :--------------------------- |
-| Create | Provides blank fields to be filled out. | Creates a new row. |
-| Update | Provides pre-populated fields based on the record that matches the row ID. | Updates the existing record. |
-| View | Provides pre-populated *read-only* fields based on the provided row ID. | N/A |
+| Type | Behavior |
+| :-- | :-- |
+| Create | Starts with blank fields and creates a new row |
+| Update | Loads row data and updates an existing row |
+| View | Loads row data as read-only |
-Next you can select a table which will provide the Datasource and Schema. This supports both the Budibase internal table, as well as the core SQL databases.
+## Schema and row ID
-If you want to use a [custom schema](https://docs.budibase.com/docs/forms#form-schema) then you will need to **Eject** the block. Alternatively you can build your own custom form using a Form *component*. This approach requires more work but is much more customisable.
+Choose a schema to generate the fields automatically.
-
+For `Update` and `View`, set the Row ID so the form knows which row to load.
-## Detail form with Row ID
-
-> 👍 Auto-configured details side panel
->
-> Manual setup is not required when creating an [autogenerated table block](https://docs.budibase.com/docs/table-block#autogenerated-screen).\
-> A [Details side panel](https://docs.budibase.com/docs/table-block#on-row-click) will already be included with the *Row ID* set to [App state](doc:app-state) of the [Clicked row](https://docs.budibase.com/docs/table#on-row-click).
-
-When selecting *Update* or *View* type, you will need to provide a *Row ID* so that the form block knows which row data to populate the fields with.
-
-
-
-### Example usage
-
-Add a [Block](doc:blocks), such as the **Cards block** for your data, and add a [Side panel](doc:side-panel) to your screen.
-
-In the Settings Panel of your **Cards block**, enter a *Title* Binding for your rows.
-
-
-
-Click on the *Click actions* button, and add an [Update state](https://docs.budibase.com/docs/data-actions#update-state) action to set a new key 'employee\_id' to `{{ Employees Cards Block.Employees._id }}`.
-
-Next click `Add Action`: **Open Side Panel**, and make sure to select your side panel from the dropdown. Then `Save` your actions.
-
-
-
-> 📘 What is '\_id'?
->
-> The **\_id** field represents a unique row identifier, which will exist on all data tables, including the internal DB and SQL tables within Budibase. It is a URL-safe encoded string.
-
-Finally add a **Form block** component inside your **Side panel**. In the Settings Panel, set *Type* to 'Update'.
-
-Provide *Row ID* with the app state binding you created above, i.e. `{{ State.employee_id }}`.
-
-
-
-You should now be able to Preview your app to see the details side panel in action:
-
-
-
-
+If you need a custom schema, eject the block and build the form manually with a Form component.
## Fields
-After you pick a table schema, you will be able to configure your fields using the right side panel.
-
-
- Form field configuration
-
+Use the settings panel to:
-Click on the cog to customise field placement, actions, validation, and much more.
-
-
- Configuring form fields
-
-
-You can adapt the layout of form fields with the layout option on each column, this allows for single, two and three column layouts.
-
-
-
-In field configuration you have a variety of things which can be done, you can use the switch to decide which fields appear in the form. Easily adjust the order by dragging items and alter the text of labels. You can also disable a field to prevent interaction or set it to read-only, maintaining the styling without allowing changes to the field's value.
-
-> 👍 Toggle all fields
->
-> If you only need to expose a couple of required fields, you can toggle the switch at the top to hide all fields, and then toggle on the few required ones.
->
->
-
-
+* Show or hide fields
+* Reorder fields
+* Change labels
+* Set fields to read-only or disabled
## Buttons
-Depending on the selected type, you will have the option to add or remove the form action buttons. For example, *Update* type will have the following buttons by default:
-
-
-
-Click the cog icon to edit the preset button [Actions](doc:actions). For example you may not want to perform navigation or show notifications after clicking the 'Save' button.
-
-You can also drag the left hand edge to re-arrange the order that the buttons appear:
-
-
-
-#### Position and size
-
-By default the buttons will appear at the bottom of your form. If you want to place them at the top, inline with your heading, then you will need to change the *Button position* to 'Top'. This can be done in the `Styles` tab.
-
-You can also enlarge the *Size* of your form, which will proportionally increase the field sizes, button sizes, and heading size.
-
-
- Large form with buttons at the top
-
-
-
+The block includes preset save buttons for the selected form type. Use the settings panel to edit the button actions or move the buttons to the top of the form.
## Ejecting
-A form block can be ejected to expose it's constituent parts. When a *Create* type form is ejected, it will appear similar to the following:
-
-
-
-The *Update* type is similar, but will also include the [Data provider](https://docs.budibase.com/docs/data-provider) and [Repeater](https://docs.budibase.com/docs/repeater) necessary to provide the row detail.
-
-
\ No newline at end of file
+Eject the block when you need full control over the underlying form components and layout.
diff --git a/docs/Apps/blocks/index.md b/docs/Apps/blocks/index.md
index 5c1bfec8a..d0997ed13 100644
--- a/docs/Apps/blocks/index.md
+++ b/docs/Apps/blocks/index.md
@@ -1,6 +1,6 @@
---
title: Blocks
-excerpt: Working with pre-built components in the Design Section
+excerpt: Working with pre-built components in the Design section
deprecated: false
hidden: false
metadata:
@@ -20,148 +20,45 @@ next:
slug: table
title: Table
---
-Blocks in the Budibase are pre-built components comprised of multiple [Components](doc:components) themselves. They perform a common functionality and take away the complexity of having to repeat yourself.
+Blocks are pre-built component groups that package common patterns into a single component.
-At the time of writing, there are seven types of blocks available.
+Use them when you want to add a complete layout faster or keep a screen easier to manage.
-
+## Add a block
-## Adding a Block
+1. Open the Design section
+2. Select `Add component`
+3. Choose a block from the `Blocks` category
-To add a block to your application, head over to the *Design* section, then press `Add component`. The blocks can be found under the *Blocks* category.
+## Configure a block
-
- List of available blocks
-
+Select the block to open its settings.
-Once you choose one of the blocks you want to have, it will appear in the design window.
+Most blocks include a `Data` setting, plus the settings from the components they contain.
-
+### Empty states
-## Configuring Blocks
+Most blocks support a custom message when no rows are returned.
-Configuration of blocks is done by clicking the block you just added to open the Settings Panel.
+Use bindings in the message if you need dynamic text.
-
- Form block
-
+## Common blocks
-The most important selection you will have to make first is picking which data table you want to use. Click the *Data* dropdown to see the list of available Datasources.
+* Cards block
+* Repeater block
+* Form block
+* Multi-step form block
+* Chart block
+* Row explorer block
-Other settings are composed of the different [Components](doc:components) that were encapsulated within the block. To learn about which components are used for each block, look at the respective section below.
+## Eject a block
-### Empty text
+Eject a block when you need to edit the nested components directly.
-Most blocks support a custom message when no data is found. This is most likely to occur when [filtering](doc:searchfilter-data) returns no results. For example:
+You can usually eject a block from the settings panel or the component tree.
-
- Empty text setting
-
+## Related pages
-
-
-You can also use Bindings in your message.
-
-## Types of blocks
-
-
-
-
-
- Name
-
-
-
- Description
-
-
-
-
-
-
-
- Cards block
-
-
-
- The *Cards Block* merges three different components into one. In order, it uses [Data Provider](doc:data-provider), [Repeater](doc:repeater) and [Card](doc:card) components.
-
- All the configuration options relevant to all three components are available in the sidebar. Additionally, you can easily add search fields to allow users to filter your card blocks.
-
-
-
-
-
- Repeater block
-
-
-
- The *Repeater Block* is a quick way to have both the [Data Provider](doc:data-provider) and the [Repeater](doc:repeater) component into one.
-
-
-
-
-
- Form block
-
-
-
- The [Form Block](https://docs.budibase.com/docs/form-block) is a comprehensive component that makes building different types of forms much more convenient.
-
-
-
-
-
- Multi-Step Form Block
-
-
-
- The Multi-Step Form Block allows large forms to be broken up into pages.
-
-
-
-
-
- Chart block
-
-
-
- The *Chart Block* combines a [Data Provider](doc:data-provider) with a nested [Chart](doc:chart) that matches the type chosen from the *Chart Type* dropdown menu.
-
-
-
-
-
- Row explorer block
-
-
-
- Allows you to view data in an alternative way. It combines a searchable and scrollable list of cards on the left hand side, whilst displaying a detail form on the right for the selected card.
-
-
-
-
-
-
-
-## Ejecting blocks
-
-As mentioned, blocks combine various components for convenience. Whilst there are many settings, sometimes blocks wont provide the full flexibility you are after. In this case, you can *eject* the block.
-
-An ejected block will split a single block component into its constituent components. To eject a block, you can press the `Eject block` button in the settings panel, or select the option from the component tree.
-
-
- Eject block button in the settings panel
-
-
-
-
-
- Eject block from component tree
-
-
-
-
-
- Ejected content
-
+* [Card](doc:card)
+* [Repeater](doc:repeater)
+* [Table](doc:table)
diff --git a/docs/Apps/blocks/more-blocks.md b/docs/Apps/blocks/more-blocks.md
index 91629efe9..1a5dd704d 100644
--- a/docs/Apps/blocks/more-blocks.md
+++ b/docs/Apps/blocks/more-blocks.md
@@ -10,62 +10,30 @@ metadata:
next:
description: ''
---
-## Repeater Block
+## Repeater block
-A repeater block and be used to iterate over rows in a table. Components nested inside a Repeater block will be repeated for as many rows are returned by the table. The bindings drawer will also have access the schema of the data source. This can be useful for creating project dashboards.
+Use a repeater block to render the same layout once for each row in a datasource.
-### Repeater Block Example
+It is useful for dashboards, card lists, and other custom row layouts.
-Add a repeater block set to the data source of the Jobs table in the Budibase sample data. Inside the repeater block, add a Container with a nested Headline component and Paragraph component, displaying the Customer Name and Job Notes, respectively.
+## Cards block
-
+Use a cards block when you want to show a small set of fields for each row, usually with an image and short summary text.
-This will display on the screen as a container-per row, with each container showing a different row from the Jobs table. With some styling applied to containers and some filtering based on dates we can very quickly produce a dashboard to show the status of jobs.
+## Chart block
-
+Use a chart block to combine a [Data provider](doc:data-provider) with a chart component.
-
+Common chart types include:
-## Cards Block
+* Pie
+* Donut
+* Bar
+* Line
+* Area
+* Histogram
+* Candlestick
-
+## Row explorer block
-Cards Blocks are a quick and easy way to display a few attributes, and an image. A great example of this would be a staff roster. By default, Cards Blocks have a Title, Subtitle, and Description, a well as an optional Image URL. This can be useful for "Meet the Team" style pages, or inventory/e-commerce applications. As with the Repeater Block, a Cards block will iterate over the rows of the data source, and bindings will be available in the bindings Drawer based on the schema of the data source.
-
-
-
-## Chart Block
-
-| Type | Description | Requirements |
-| :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------ |
-| Pie | Circular graphic divided into "slices", where the greater the arc-length is proportional to the numerical value. | Label (text), Data (numerical) |
-| Donut | Similar to the Pie Chart, a Donut Chart is a segmented ring, where segment-sizes are proportional to the numerical value. | Label (text), Data (numerical) |
-| Bar | Presents categorical data with rectangular bars with heights or lengths proportional to the values that they represent. | Label (text), Data (numerical) |
-| Line | Displays information as a series of data points connected by straight line segments | Label (text), Data (numerical) |
-| Area | Similar to a line chart, an area chart also shades the area beneath the line. Multiple data lines can be chosen for display. The order in which data lines are chosen will determine which shaded area shows over others. | Label (text), Data (numerical) |
-| Histogram | | Value (numerical) |
-| Candlestick | Typically used to show share prices | Open price, High price, Low price, Close price (all numerical), and Date. |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Row Explorer
-
-
-
-A row explorer block helps give a brief overview of all rows in a datasource, and then a closer look at one row at a time. Built into the block is a search-field, as well as an update-row form.
-
-
\ No newline at end of file
+Use a row explorer block to show a list of rows on one side and the selected row details on the other.
diff --git a/docs/Apps/blocks/multi-step-form-block.md b/docs/Apps/blocks/multi-step-form-block.md
index d57b2a1b0..5b4a53bc1 100644
--- a/docs/Apps/blocks/multi-step-form-block.md
+++ b/docs/Apps/blocks/multi-step-form-block.md
@@ -10,108 +10,38 @@ metadata:
next:
description: ''
---
-When building a [Form](doc:forms) with many [Form steps](doc:form-steps) the Component Tree can get quite long and tricky to maintain. The multi-step form block aims to solve this problem by allowing you to manage your form steps within the Settings Panel.
-
-
+Use the Multi-step form block when you want to manage step-based forms in one component.
## General settings
-When you initially create a multi-step form block, it will have a single step displaying all the form fields for the chosen *Data* source. Blocks support the internal Budibase DB tables, as well as [SQL](doc:sql-datasource) tables.
-
-Just like a [Form block](doc:form-block) you may change the form *Type* between 'Create', 'Update', and 'View' as needed.
-
-The selected data source and form type will be applied across all form steps in your multi-step form block.
-
-
- General settings
-
-
-
+Choose the data source and form type once. These settings apply to every step in the block.
-## Details
+Supported form types:
-The details section includes settings that will be applied on a per step basis. Initially there will be a single step, but you can click to `Add step` button to get started.
+* Create
+* Update
+* View
-
- Add a form step
-
+## Steps
-Once another form step has been added, you will be able to navigate between the steps using arrow buttons. The `x` button will delete a form step, while the `+` icon will add another step immediately after the currently selected step.
+Start with one step, then add more as needed. Use the step controls to move between steps, add steps, or delete steps.
-
- Navigating between form steps
-
+Step titles can use the current step binding:
-#### Title and description
-
-By default the title of each step will be tied to current step Binding like so:
-
-```
+```text
Step {{ Multi-step Form Block.Current Step }}
```
-This will conveniently label each step "Step 1", "Step 2", etc without needing manual input. With that said, you can easily replace this with your own step title.
-
-Similarly you can add a *Description* which will appear below the step title, however this is blank by default.
-
-
- Adding a description
-
-
-
- Step title with description
-
-
-#### Fields
-
-Form fields can be shown/hidden and further configured for each form step. For example you may want to show only 'contact information' fields in your first step, while showing only 'address details' fields in the second step.
-
-Read [configuring form fields](https://docs.budibase.com/docs/form-block#fields) for more information.
-
-#### Buttons
-
-When you add new steps in the multi-step form block, Budibase will intelligently configure the buttons you would likely want as follows:
-
-
-
-
-
- First step
-
-
-
- Middle step
-
+You can also add a description for each step.
-
- Last step
-
-
-
+## Fields
-
-
-
-
-
+Show or hide fields per step so each step only contains the inputs it needs.
-
-
-
+## Buttons
-
-
-
-
-
-
+Budibase adds the usual step navigation buttons automatically. The final step keeps the save action, while earlier steps use step navigation actions.
-These buttons will use the [Change Form Step](https://docs.budibase.com/docs/form-actions#change-form-step) action to appropriately navigate between the different steps, and the 'Save' button will naturally save your form at the end. If you need custom actions, click on the cog icon beside a button. You may also add and remove buttons as needed.
+## Initial step
-Read [configuring form buttons](https://docs.budibase.com/docs/form-block#buttons) for more information.
\ No newline at end of file
+Set the initial step when you want the form to open on something other than step 1, such as when using persisted state.
diff --git a/docs/Apps/blocks/table-block.md b/docs/Apps/blocks/table-block.md
index 2f85c28d1..4723d9c72 100644
--- a/docs/Apps/blocks/table-block.md
+++ b/docs/Apps/blocks/table-block.md
@@ -1,6 +1,6 @@
---
title: Table Block (Deprecated)
-excerpt: Quickly display and action your data
+excerpt: Quickly display and act on your data
deprecated: false
hidden: true
metadata:
@@ -10,78 +10,28 @@ metadata:
next:
description: ''
---
-The Table block effectively merges both the [Data Provider](doc:data-provider) and the [Table](doc:table) component into one. It also integrates [Searching and filtering data](doc:searchfilter-data). This helps you simplify the logic of the page, and not have to worry about configuring two different components.
+The Table block combined a [Data provider](doc:data-provider) and a [Table](doc:table) component.
-Furthermore, it allows you to add CRUD (Create, Read, Update and Delete) functionality to your table data; all in one component! It achieves this through the use of 'Create' and 'Edit Detail' [Side panels](doc:side-panel).
+It also included search, filtering, and row actions in one block.
-Many of the settings will be shared with the [Table](doc:table) component. The block specific settings are described on this page.
-
-> 👍 Table
->
-> The new table documentation can be found [here](doc:table).
+Use the current [Table](doc:table) documentation instead.
## Search fields
-Select the fields you want users to be able to search on. For a [Text field](doc:text-field) the search will be a *starts with*. Other fields will search for an exact match.
-
-As noted under the setting, a maximum of five search fields can be used at any given time.
-
-
- Allow users to search on First Name and Start Date
-
-
-
-
-If you add search fields, upon [Ejecting](https://docs.budibase.com/docs/blocks#ejecting-blocks) a [Form](doc:forms) component will be added like so:
-
-
- Ejected table block with search fields
-
-
-In addition, the added [Data provider](doc:data-provider) will include the necessary filters with the form field bindings.
-
-
-
-## On row click
-
-This setting allows you to determine what happens when a table row is clicked. By default **Run actions** will be checked, however with *no actions set* nothing will happen.
-
-
- Run actions on row click
-
-
-Click `Define actions` to get access to all of the available [Actions](doc:actions).
-
-The other option is **Show details side panel**. This will open a [Side panel](doc:side-panel) when a row is clicked, showing the row detail within a [Form block](doc:form-block).
-
-As noted under the option, table block side panels will not work with [REST queries](doc:rest-queries) or other [Custom queries](https://docs.budibase.com/docs/data-sources#custom-queries).
-
-## Show button
-
-You can add an action button to the top right of your table. To do so, check `Show button above table`.
-
-
- The table block will have no action button by default
-
-
-Once checked, you will be able to enter some text for the button, and similar to *on row click*, you can choose between running some actions or opening a side panel.
+Search fields let users filter table rows by specific columns.
-In this case however, the side panel will have a nested form block in *Create* mode, i.e. the fields will initially be blank and save a new row on submit, rather than update an existing row.
+Text fields use a starts-with match. Other fields use an exact match.
-
- Action button opens a new row side panel
-
+## Row click
-
- Table block with Create row action button
-
+Use row click actions to run actions or open a side panel.
-## Autogenerated screen
+## Button actions
-When creating an autogenerated screen, the table block is the only component that is added, with both side panel options selected.
+You can add a button above the table to run actions or open a create side panel.
-Ejecting an autogenerated screen table block will result in a component tree like so:
+## Autogenerated screens
-
+Autogenerated screens use the Table block by default.
-Ejecting in this case is useful if you wanted to view and edit the side panel forms within the builder.
\ No newline at end of file
+If you eject the block, you can edit the nested components directly.
diff --git a/docs/Apps/chart/area-chart.md b/docs/Apps/chart/area-chart.md
index 48c7ff0da..93a4bbd2b 100644
--- a/docs/Apps/chart/area-chart.md
+++ b/docs/Apps/chart/area-chart.md
@@ -10,181 +10,41 @@ metadata:
next:
description: ''
---
-The area chart is a mixture between the [Bar Chart](doc:bar-chart) and the [Line Chart](doc:line-chart). Allowing you to display multiple columns of data on top of each other, having the area coloured.
+Use an area chart to compare one or more numeric series across the same labels.
-
+Area charts are useful when you want stacked or overlapping lines with filled areas.
-Not only can you stack the different data like above, but you can also let them overlay.
+## Setup
-## Adding an Area Chart
+1. Add a [Data provider](doc:data-provider)
+2. Add an Area chart inside the provider
+3. Select the provider in the chart settings
+4. Choose a label column
+5. Choose one or more numeric data columns
-To add the area chart to your UI, you first need to wrap it in a [Data Provider](doc:data-provider). Then you can add the Area chart to your UI through `Chart > Area Chart`.
+## Settings
-
+Common settings include:
-## Displaying data
+* Provider
+* Label column
+* Data column(s)
+* Format
+* X-axis and Y-axis labels
+* Width and height
+* Colours
+* Curve
+* Data labels
+* Animate
+* Legend
+* Stacked
-After adding the Area chart to your UI, you can now select data. You should've already selected a data source through the [Data Provider](doc:data-provider), so now you can select the Data Provider in the Area chart.
+## Use stacked values
-In the chart on top of this page there are 4 columns selected, and they stack by default. To do this, just select the data provider, a label column, and select the 4 columns you want to display.
+Enable `Stacked` when you want series to build on top of each other.
-
+Leave it off when you want the series to overlap.
-And that is all you need to do to get an area chart set up. But of course, you customize much more in the Settings Panel. To read more about further customization, check the reference table below.
+## Requirements
-> 📘 Numeric data only
->
-> Of course, line charts need to have a scale, so for the data column(s), you can only select [Numeric](doc:number) columns (eg; containing values that can be converted to numbers)
-
-## Settings panel
-
-The settings panel has quite a few customization options, allowing you to get the most out of your bar chart.
-
-
-
-
-
- Setting
-
-
-
- Description
-
-
-
-
-
-
-
- Provider
-
-
-
- The [Data Provider](doc:data-provider) as a parent of your chart
-
-
-
-
-
- Label Column
-
-
-
- The Labels you want to display with your data. These are displayed on the X-axis
-
-
-
-
-
- Data Column(s)
-
-
-
- The data you want to display as lines. This has to be [numeric](doc:number). Can be multiple
-
-
-
-
-
- Format
-
-
-
- Will divide labels by thousand or million on the axis and adds a `K` or `M` to the axis respectively
-
-
-
-
-
- Y-axis Label\
- X-axis label
-
-
-
- The labels you want to display next to the Y and X-axis.
-
-
-
-
-
- Width\
- Height
-
-
-
- The size you want the chart to be, supports all CSS supported sizes
-
-
-
-
-
- Colours
-
-
-
- Choose from one of the predefined colour-palettes
-
-
-
-
-
- Curve
-
-
-
- The type of line you want to have.
-
- * \*Smooth\*\*: A curved line flowing with the data
- * \*Straight\*\*: A line straight from point to point
- * \*Stepline\*\*: A line which remains horizontal, and drops down completely for the next data point. See below the reference table for an example of all three.
-
-
-
-
-
- Data Labels
-
-
-
- Will display the value on every point on the graph
-
-
-
-
-
- Animate
-
-
-
- Will animate when the data changes or on startup. Data-change can happen through [Dynamic filter's](doc:dynamic-filter) for example.
-
-
-
-
-
- Legend
-
-
-
- Will display, in the upper-right corner, the label for each of the data columns displayed on the chart
-
-
-
-
-
- Stacked
-
-
-
- Check this setting to have the different data columns stacked, which means they won't overlay but be stacked on top of each other.\
- You can see the difference between stacked and not stacked below the reference table
-
-
-
-
-
-### Stacked vs non-stacked comparison
-
-
- Not stacked (left) and Stacked (right) comparison
-
\ No newline at end of file
+Area chart data columns must be numeric.
diff --git a/docs/Apps/chart/bar-chart.md b/docs/Apps/chart/bar-chart.md
index 380080eb7..a94ec1157 100644
--- a/docs/Apps/chart/bar-chart.md
+++ b/docs/Apps/chart/bar-chart.md
@@ -10,172 +10,41 @@ metadata:
next:
description: ''
---
-The bar chart is a very basic, but powerful, method of displaying data in such a way it makes much more sense immediately, compared to simply browsing the data in tables.
+Use a bar chart to compare numeric values across categories.
-
+## Setup
-## Adding a Bar Chart
+1. Add a [Data provider](doc:data-provider)
+2. Add a Bar chart inside the provider
+3. Select the provider in the chart settings
+4. Choose a label column
+5. Choose one or more numeric data columns
-To add a bar chart to your UI, you need to connect it to your data source using a [Data Provider](doc:data-provider). After adding the `data provider`, add the `Chart > Bar Chart` component to it, and you'll be able to link up the data from your table.
+## Settings
-
+Common settings include:
-> 👍 Grouping data
->
-> To group data inside a bar chart, use [Views](doc:views) on top of your data. This way, you can hook up your chart using the data provider linked to the view instead.
+* Provider
+* Label column
+* Data column(s)
+* Format
+* X-axis and Y-axis labels
+* Width and height
+* Colours
+* Stacked
+* Horizontal
+* Data labels
+* Animate
+* Legend
-## Displaying data
+## Use horizontal bars
-Now that you have the data linked up to your bar chart, you can select which columns you want to display. For example, we have a table with cars and their mileage, and displaying this in a bar chart is only a few clicks.
+Enable `Horizontal` when you want labels on the Y-axis and bars that run left to right.
-
+## Group data
-To get this view, head over to the Settings Panel, and select the `License Plate` as the label column, and the mileage as the data column(s). And to get the bars horizontally, instead of vertical, check the `Horizontal` setting in the Settings Panel. And there you have it.
+If you want grouped values, use a [View](doc:views) or other grouped datasource before connecting the chart.
-This is just one of the many things you can do with the bar chart, explore all the features from the Settings Panel below.
+## Requirements
-> 📘 Numeric data only
->
-> Of course, bars need to have a size, so for the data column(s), you can only select [Numeric](doc:number) columns (eg; containing values that can be converted to numbers)
-
-## Settings panel
-
-The settings panel for the bar chart has the following options. Generic styling settings are omitted from this overview.
-
-
-
-
-
- Setting
-
-
-
- Description
-
-
-
-
-
-
-
- Provider
-
-
-
- The [Data Provider](doc:data-provider) as a parent of your chart
-
-
-
-
-
- Label Column
-
-
-
- The Labels you want to display with your data. These are on the X-axis in standard mode, or on the Y-axis on `Horizontal` mode
-
-
-
-
-
- Data Column(s)
-
-
-
- The data you want to display with the labels. This has to be numeric. Can be multiple
-
-
-
-
-
- Format
-
-
-
- Will divide by thousand or million on the axis and adds a `K` or `M` to the axis respectively
-
-
-
-
-
- Y-axis Label\
- X-axis label
-
-
-
- The labels you want to display next to the Y and X-axis.
-
-
-
-
-
- Width\
- Height
-
-
-
- The size you want the chart to be, supports all CSS supported sizes
-
-
-
-
-
- Colours
-
-
-
- Choose from one of the predefined colour-palettes
-
-
-
-
-
- Stacked
-
-
-
- Check this setting to have the different data columns stacked (aka, on the same line instead of each column as a separate bar) in the chart
-
-
-
-
-
- Horizontal
-
-
-
- Displays the bars horizontally, rather than vertically, when checked. The labels will move to the Y-axis when this is checked
-
-
-
-
-
- Data Labels
-
-
-
- Will display the value of the bar on top of the bar when checked
-
-
-
-
-
- Animate
-
-
-
- Will animate when the data changes or on startup. Data-change can happen through [Dynamic filter's](doc:dynamic-filter) for example.
-
-
-
-
-
- Legend
-
-
-
- Displays the label for each colour visible on the chart as a legend in the upper-right corner
-
-
-
-
\ No newline at end of file
+Bar chart data columns must be numeric.
diff --git a/docs/Apps/chart/candlestick-chart.md b/docs/Apps/chart/candlestick-chart.md
index fb9d10920..1d28b37a7 100644
--- a/docs/Apps/chart/candlestick-chart.md
+++ b/docs/Apps/chart/candlestick-chart.md
@@ -10,153 +10,40 @@ metadata:
next:
description: ''
---
-The Candlestick chart is a chart type often used in financial charts. You can configure both a bar and a line on the same row.
+Use a candlestick chart to display open, close, high, and low values over time.
-
+This chart is commonly used for financial data.
-There's a lot of data being displayed on a single column, so when you want to use this graph you need to have access to four different [Numeric](doc:number) data-points per column. This could also be a great candidate to transform the data through [Views](doc:views) when you want to do some calculation based on data points you have in your database.
+## Setup
-## Adding a Candlestick Chart
+1. Add a [Data provider](doc:data-provider)
+2. Add a Candlestick chart inside the provider
+3. Select the provider in the chart settings
+4. Choose the date column
+5. Map the open, close, high, and low columns
-To add the Candlestick chart, first place a [Data Provider](doc:data-provider) and add the chart nested to it. To add the chart, click `Chart > Candlestick Chart`
+## Data requirements
-
+Candlestick charts need:
-## Displaying data
+* A date column
+* Open
+* Close
+* High
+* Low
-Now that the Candlestick chart has been added to the UI, you can display data. For this type of chart you'll need 4 types of [Numeric](doc:number) datapoints.
+All numeric fields must contain values that can be converted to numbers.
-These 4 data points, or `columns`, are for the following fields:
+## Settings
-| Setting | Description |
-| :------ | :----------------------------------------- |
-| Open | The opening for the `bar` inside the chart |
-| Close | The closing for the `bar` inside the chart |
-| High | The high for the `line` inside the chart |
-| Low | The low for the `line` inside the chart |
+Common settings include:
-> 👍 What determines the colour?
->
-> When colour will be red or green depending on the `open` and `close` values. If `close` is a lower number than `open`, the colour will be red. If the `close` is higher than the `open` then the colour will be green. This also applies to the line in the chart, not just the bar.
+* Provider
+* Format
+* X-axis and Y-axis labels
+* Width and height
+* Animate
-The labels on the x-axis are supposed to be dates, this is a financial chart after all. So when picking the `Date column`, make sure you use a [Date/time](doc:datetime) field for best results.
+## Color behavior
-There aren't a whole lot of other customization options in the Settings Panel for the Candlestick chart, but of course, there are some. You can find them in the reference table below.
-
-## Settings panel
-
-> 📘 Data columns
->
-> There are 4 data column fields, which are explained in the `Displaying data` section above.
-
-Besides the settings already explained, there are a few which you can still configure in the Settings Panel.
-
-
-
-
-
- Setting
-
-
-
- Description
-
-
-
-
-
-
-
- Provider
-
-
-
- The [Data Provider](doc:data-provider) as a parent of your chart
-
-
-
-
-
- Format
-
-
-
- Will divide by thousand or million on the axis and adds a `K` or `M` to the axis respectively
-
-
-
-
-
- Y-axis Label\
- X-axis label
-
-
-
- The labels you want to display next to the Y and X-axis.
-
-
-
-
-
- Width\
- Height
-
-
-
- The size you want the chart to be, supports all CSS supported sizes
-
-
-
-
-
- Animate
-
-
-
- Will animate when the data changes or on startup. Data-change can happen through [Dynamic filter's](doc:dynamic-filter) for example.
-
-
-
-
-
-## Tutorial
-
-### Getting Started
-
-Head to [https://www.alphavantage.co/](https://www.alphavantage.co/) and get an API key
-
-In the data tab, create a new data source using the REST API connector and build a query.
-
-
-
-Your query should include the function, symbol, and interval parameters, as well as using your API. Click "Send" to verify a successful connection.
-
-The response-data from the API isn't quite in the right format for Budibase to make use of, so you will need to manipulate it a little using the Transformer.
-
-
-
-```
-let apiResponse = data
-
-const transformedArray = Object.keys(apiResponse["Time Series (5min)"]).map(date => {
- const data = apiResponse["Time Series (5min)"][date];
- return {
- date: date,
- open: data["1. open"],
- high: data["2. high"],
- low: data["3. low"],
- close: data["4. close"],
- volume: data["5. volume"]
- };
-});
-
-return transformedArray
-```
-
-When your transformer is set up, click "Send" to test the query. The response will be an array of objects, but the JSON window will show you an example of a single object. Be sure to save your query before navigating away.
-
-### Displaying the Data
-
-In the design tab, on the screen you wish to display data on, add a Data Provider and set it to fetch information from this query. Now add a Candlestick Chart set to receive data from your Data Provider. Once you have set the sources for Date, Open, High, Low, and Close, your chart will appear.
-
-
\ No newline at end of file
+The chart uses one color when the close is lower than the open and another when the close is higher.
diff --git a/docs/Apps/chart/gauge-chart.md b/docs/Apps/chart/gauge-chart.md
index 2e588dcff..2d65fedf8 100644
--- a/docs/Apps/chart/gauge-chart.md
+++ b/docs/Apps/chart/gauge-chart.md
@@ -6,32 +6,20 @@ metadata:
description: kba_5h8oy
robots: index
---
-A gauge chart displays progress or performance for multiple items simultaneously using coloured circular arcs. Each ring represents a separate entity (e.g., a user, task, or metric), and the length of the coloured arc shows how much of the target has been achieved
+Use a gauge chart to show progress or completion for one or more values.
-
+## Setup
-
+1. Add a [Data provider](doc:data-provider)
+2. Add a Gauge chart inside the provider
+3. Select the provider in the chart settings
+4. Choose a label column
+5. Choose the numeric value column
-
+## Common use
-## Tutorial
+Gauge charts are useful for progress indicators, quotas, and status summaries.
-For this example, the gauge chart will be used to calculate the usage of each user's annual leave (e.g 0% = no annual leave taken, 100% = all annual leave taken)
+## Requirements
-First we need create a data table, below I have created a table consisting of users, days taken, days allowed, and days remaining
-
-
-
-Then create a new app, and add a data provider, followed by the gauge chart component. For the data provider, set the data source to be our table 'days off', then in the nested gauge chart, set the following settings:
-
-
-
-The provider will be the datasource we are taking this information from, the label column in this example will be the names of the users we have in our table, but you could also use dates, numbers and boolean's. The data column is our 'remaining' column, which shows the amount of days left
-
-Now if we hover over any of the rings they will display a percentage, for example, if we hover over Fiona, we can see she has used 43.33% of her total annual leave
-
-
-
-
-
-
\ No newline at end of file
+Gauge chart value columns must be numeric.
diff --git a/docs/Apps/chart/histogram-chart.md b/docs/Apps/chart/histogram-chart.md
index 21bcf5e1b..3fbe54c27 100644
--- a/docs/Apps/chart/histogram-chart.md
+++ b/docs/Apps/chart/histogram-chart.md
@@ -10,16 +10,25 @@ metadata:
next:
description: ''
---
-A histogram allows you to display the frequency of data within ranges, known as buckets. For example, here is a histogram showing the frequency of sales for different unit cost buckets.
+Use a histogram to show how often values fall into numeric ranges.
-
+## Setup
-## Displaying data
+1. Add a [Data provider](doc:data-provider)
+2. Add a Histogram chart inside the provider
+3. Select the provider in the chart settings
+4. Choose the numeric value column
+5. Set the bucket count
-Add the **Chart Block** or a [Data provider](doc:data-provider) to your screen, and select the data source, which could be a table, view, or [Custom query](https://docs.budibase.com/docs/data-sources#custom-queries). Apply any limits or filtering to the data source that you want.
+## Settings
-Next in the Settings Panel of the histogram component, select the *Value column*. This field must be a [Number](doc:number), and will determine the range for each bucket.
+Common settings include:
-The number of buckets can be adjusted by the *Bucket count*. A checkbox is also available to determine if you want your Histogram to be displayed horizontally or vertically. You can provide labels for the x and y axis.
+* Value column
+* Bucket count
+* Horizontal or vertical layout
+* X-axis and Y-axis labels
-
\ No newline at end of file
+## Requirements
+
+Histogram value columns must be numeric.
diff --git a/docs/Apps/chart/index.md b/docs/Apps/chart/index.md
index 6d49088e7..947b730f6 100644
--- a/docs/Apps/chart/index.md
+++ b/docs/Apps/chart/index.md
@@ -1,6 +1,6 @@
---
title: Charts
-excerpt: Visualise your data
+excerpt: Visualise your data.
deprecated: false
hidden: false
metadata:
@@ -10,56 +10,44 @@ metadata:
next:
description: ''
---
-Whilst there are a variety of chart types, all charts have some common properties:
+Use charts to present numeric data from a [Data provider](doc:data-provider).
+
+All charts share the same core settings:
* Title
* Provider
-* Label Column
-* Data Column(s)
-* Width & Height
+* Label column
+* Data column(s)
+* Width and height
* Margin
* Custom CSS
* Conditions
-## Providing data
+## Choose a source
Charts allow you to visualise data from a [Data Provider](doc:data-provider), which can have datasources such as:
-
-* Budibase DB
+
+* Budibase DB
+* External Data Tables like MS SQL
* [REST Queries](doc:rest-queries)
* [Custom Queries](doc:custom-queries)
+* JSON/CSV
-## Displaying data
-
-The data displayed in your chart will be determined by the label and data columns.
-
-**Label Column** Provides the name of each categorical variable.\
-**Data Column(s)** Provides the value or range of values for each label.
-
-> 🚀 Data column type
->
-> Data columns must be numeric (eg; containing values that can be converted to numbers)
-
-## Generating the chart schema
-
-For table datasources, the available options for the label and data columns will match the columns of the table.
-
-When using a **View** as a datasource, any **View Calculations** (such as Sum, Average, Minimum, or Maximum) you have configured will also be available as numeric data columns for your chart.
-
-Data from REST Queries on the other hand, will likely need to be transformed to be suitable for use in charts.
-
-In particular, your [Transformer](doc:transformers) and Schema should align with the following:
-
-
-
-Naturally you would replace the strings and numbers with response data from your API.
+## Use chart data
-### Many data columns
+The label column names each category.
-Most chart types can make use of more than one value column. Thus, additional value fields would be needed in your schema.
+The data column or columns provide the numeric values for each label.
-For example, you may want to display the change in average temperature between seasons for each country:
+REST queries and custom queries often need a transformer so the returned data matches the chart schema.
-
+## Common chart types
-
+* Area
+* Bar
+* Candlestick
+* Gauge
+* Histogram
+* Line
+* Pie
+* Progress donut
diff --git a/docs/Apps/chart/line-chart.md b/docs/Apps/chart/line-chart.md
index e81da516c..bced2c491 100644
--- a/docs/Apps/chart/line-chart.md
+++ b/docs/Apps/chart/line-chart.md
@@ -10,164 +10,40 @@ metadata:
next:
description: ''
---
-The Line Chart component is an easy way to visualize progression easily. For example, if you want to plot the amount of applications you have received per day, you could get something like this.
+Use a line chart to show change over time or across an ordered category.
-
+## Setup
-## Adding a Line Chart
+1. Add a [Data provider](doc:data-provider)
+2. Add a Line chart inside the provider
+3. Select the provider in the chart settings
+4. Choose a label column
+5. Choose one numeric data column
-To add a line chart, head over to the design section in the Budibase UI. Then, you'll need a wrapping [Data Provider](doc:data-provider). After you've added the data provider, add a `Chart > Line Chart` to the design as a child of the data provider. This should connect your chart to the database directly.
+## Settings
-
+Common settings include:
-Then, in the Settings Panel you will be able to configure your chart to display exactly what you want. First, you want to select the correct [Data Provider](doc:data-provider), if you have more than one as a parent. The second step is to select a `Label column`. The Label Column defines the labels on your X-axis. Lastly, you'll want to select a `Data column`. This column will represent the data drawn in the chart. For this to work the column will of course have to be numeric.
+* Provider
+* Label column
+* Data column(s)
+* Format
+* X-axis and Y-axis labels
+* Width and height
+* Colours
+* Curve
+* Data labels
+* Animate
+* Legend
-And that's it, you have your line chart. Want to understand all the possible customization settings? Check the reference below.
+## Curve types
-> 📘 Numeric data only
->
-> Of course, line charts need to have a scale, so for the data column(s), you can only select [Numeric](doc:number) columns (eg; containing values that can be converted to numbers)
->
-> ![]()
+Choose the curve style that best matches the chart:
-## Settings panel
+* Smooth
+* Straight
+* Stepline
-The settings panel has quite a few customization options, allowing you to get the most out of your line chart.
+## Requirements
-
-
-
-
- Setting
-
-
-
- Description
-
-
-
-
-
-
-
- Provider
-
-
-
- The [Data Provider](doc:data-provider) as a parent of your chart
-
-
-
-
-
- Label Column
-
-
-
- The Labels you want to display with your data. These are displayed on the X-axis
-
-
-
-
-
- Data Column(s)
-
-
-
- The data you want to display as lines. This has to be [numeric](doc:number). Can be multiple
-
-
-
-
-
- Format
-
-
-
- Will divide labels by thousand or million on the axis and adds a `K` or `M` to the axis respectively
-
-
-
-
-
- Y-axis Label\
- X-axis label
-
-
-
- The labels you want to display next to the Y and X-axis.
-
-
-
-
-
- Width\
- Height
-
-
-
- The size you want the chart to be, supports all CSS supported sizes
-
-
-
-
-
- Colours
-
-
-
- Choose from one of the predefined colour-palettes
-
-
-
-
-
- Curve
-
-
-
- The type of line you want to have.
-
- * \*Smooth\*\*: A curved line flowing with the data
- * \*Straight\*\*: A line straight from point to point
- * \*Stepline\*\*: A line which remains horizontal, and drops down completely for the next data point. See below the reference table for an example of all three.
-
-
-
-
-
- Data Labels
-
-
-
- Will display the value on every point on the graph
-
-
-
-
-
- Animate
-
-
-
- Will animate when the data changes or on startup. Data-change can happen through [Dynamic filter's](doc:dynamic-filter) for example.
-
-
-
-
-
- Legend
-
-
-
- Will display, in the upper-right corner, the label for each of the data columns displayed on the chart
-
-
-
-
-
-## Line Chart curve types
-
-There are three kinds of curves you can choose from in a line chart. In the image below you can see all three, with the same data-set, as a comparison.
-
-
\ No newline at end of file
+Line chart data columns must be numeric.
diff --git a/docs/Apps/chart/pie-donut-chart.md b/docs/Apps/chart/pie-donut-chart.md
index ae6dd030d..f7b229f58 100644
--- a/docs/Apps/chart/pie-donut-chart.md
+++ b/docs/Apps/chart/pie-donut-chart.md
@@ -10,123 +10,31 @@ metadata:
next:
description: ''
---
-The Pie and Donut Charts are both circular charts. They're great in visualizing percentages, like how much of a pie you're going to get.
+Use pie and donut charts to compare parts of a whole.
-They both behave and look the same, with the exception of the shape. The Donut Chart has a hole in the middle, where the Pie chart does not.
+Pie and donut charts use the same settings. The donut chart just leaves a hole in the center.
-
- Comparison of the Pie Chart (left) and the Donut Chart (right)
-
+## Setup
-## Adding a Pie or Donut Chart
+1. Add a [Data provider](doc:data-provider)
+2. Add a Pie chart or Donut chart inside the provider
+3. Select the provider in the chart settings
+4. Choose a label column
+5. Choose a numeric data column
-To add a Pie or Donut chart, head over to the design section in the Budibase UI. Then, you'll need a wrapping [Data Provider](doc:data-provider). After you've added the data provider, add a `Chart > Pie Chart` or `Chart > Donut Chart` to the design as a child of the data provider. This should connect your chart to the database directly.
+## Settings
-
+Common settings include:
-Then, in the Settings Panel, you will want to configure how to display the data. This kind of chart is probably often filled with data coming from a [View](doc:views), but can of course be connected directly to a table as well.
+* Provider
+* Label column
+* Data column
+* Colours
+* Width and height
+* Data labels
+* Animate
+* Legend
-First, in the `Label column` setting, you want to select which column represents the label of the data you want to display, then in the `Data column` you want to select the column which represents data in a [Number](doc:number) format to display in the chart. Once this is done, your chart should already be ready to view!
+## Requirements
-For more customization, check the reference table below.
-
-## Settings panel
-
-The settings panel has quite a few customization options, allowing you to get the most out of your pie or donut chart.
-
-
-
-
-
- Setting
-
-
-
- Description
-
-
-
-
-
-
-
- Provider
-
-
-
- The [Data Provider](doc:data-provider) as a parent of your chart
-
-
-
-
-
- Label Column
-
-
-
- The Labels you want to display with your data. These are displayed on the legend
-
-
-
-
-
- Data Column
-
-
-
- The data you want to display as the segments. This has to be [numeric.](doc:number)
-
-
-
-
-
- Colours
-
-
-
- Choose from one of the predefined colour-palettes
-
-
-
-
-
- Width\
- Height
-
-
-
- The size you want the chart to be, supports all CSS supported sizes
-
-
-
-
-
- Data Labels
-
-
-
- Shows the percentage of each section on the segment.
-
-
-
-
-
- Animate
-
-
-
- Will animate when the data changes or on startup. Data-change can happen through [Dynamic filter's](doc:dynamic-filter) for example.
-
-
-
-
-
- Legend
-
-
-
- Will display, in the upper-right corner, the label for each of the data columns displayed on the chart
-
-
-
-
\ No newline at end of file
+Pie and donut chart data columns must be numeric.
diff --git a/docs/Apps/chart/progress-donut-chart.md b/docs/Apps/chart/progress-donut-chart.md
index 971bd7dad..342d23710 100644
--- a/docs/Apps/chart/progress-donut-chart.md
+++ b/docs/Apps/chart/progress-donut-chart.md
@@ -10,74 +10,23 @@ metadata:
next:
description: ''
---
-It is possible to display a progress bar using a [Donut Chart](doc:pie-donut-chart).
+Use a progress donut chart to show completion against a target.
-This can be useful when building KPI dashboards or other task tracking apps.
+This is useful for dashboards and task tracking screens.
-## Data
+## Setup
-You may have a table with tasks, each with a status such as:
+1. Prepare a datasource with grouped progress values
+2. Add a [Data provider](doc:data-provider)
+3. Add a Donut chart inside the provider
+4. Select the progress view or query as the provider source
+5. Bind the status or group field to the label column
+6. Bind the numeric count or total to the data column
-
+## Common use
-If you don't already have data like this but still want to continue with the walkthrough:
+Progress donut charts are often used to show task statuses, completion rates, or any grouped progress summary.
-* Create Head to the Data section and create a new table in your workspace
-* Create a text-column, and a Single-Select column.
-* In the Single-Select column, set the possible options for your tasks that best suit your needs. Here we'll be using "Pending", "In Progress", and "Complete".
-* Fill in with some sample tasks and set their statuses.
+## Optional interactions
-### Preparing the Data
-
-To provide our progress chart with the correct data, we will need to create a [View](doc:views) so that the tasks can be grouped by status.
-
-First click the 'Create new view' button, select 'Calculation', and set the name of the view to something descriptive like "Tasks Progress Report"
-
-
-
-You'll be met with a very blank table - don't panic, we need to configure our calculations. Click "Configure Calculations". In this case we want to count how many tasks have each status. This is almost read as a sentence when we see the calculation is "Calculate the" `Count` "of" `task` `Group by` "status" - we're counting how many tasks have a particular status.
-
-
-
-With my sample data, I can then see that I've got 3 rows, one for each status, and the "Count task" tells me how many tasks have that status assigned.
-
-
-
-## Apps > Design
-
-In your workspace, add an App and create a new screen. Add a [Data Provider](doc:data-provider) with the view as its datasource.
-
-
-
-Nested underneath, add your donut chart with the provider set.\
-For the label column select *Status* and for the data column select *Count Task*.
-
-
-
-
-
-Adjust the settings as desired, for example setting colours specific to the status.
-
-
-
-### Adding more functionality
-
- Using the Donut Chart's "On Click Actions" we can display the data that a user clicks on. Add an "On Click Segment" action and set the Clicked Segment's status into state. This will set the string "In Progress" in state, when the "In Progress" segment is clicked.
-
-
-
-Next, in your Screen, add a repeater block set to display the Tasks table (not the Tasks view calculation). Use a filter on the Status column to match the `clicked_tasks` variable we set in state.
-
-
-
-Now in your Repeater Block, you'll have every task with the status that the user clicks on in the donut chart. Add a Text component to the repeater block and use it to display your task and status.
-
-
-
-
-
-## External datasource
-
-Instead of using an internal table view, you may wish to use a [Custom query](doc:custom-queries) from an external datasource.
-
-You will need to transform the data in the format outlined here, where there is a label for each status.
\ No newline at end of file
+You can use click actions on a donut chart to update state or filter other components on the screen.
diff --git a/docs/Apps/components/accordion.md b/docs/Apps/components/accordion.md
index 7d4c7c18e..3961c6dab 100644
--- a/docs/Apps/components/accordion.md
+++ b/docs/Apps/components/accordion.md
@@ -10,70 +10,35 @@ metadata:
next:
description: ''
---
-The Accordion component is a collapsible container allowing users to toggle its body content's visibility. It is particularly useful for organising content in a space-efficient manner, such as FAQs, settings panels, or grouped information.
+Use the Accordion component to hide and reveal grouped content.
## Features
-* **Collapsible Sections**: Users can expand or collapse content panels to show or hide information.
-* **Customisable Header**: The accordion heading can be customised with a label and styling options.
-* **Dynamic Content**: Supports nested components or any child content within the expandable body.
+* Collapsible sections
+* Customisable header
+* Nested child content
## Settings
-| Setting | Description |
-| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| Label | A text string that serves as the heading for the accordion section. This label is always visible, whether the accordion is expanded or collapsed. |
-| Bold | A boolean option that, when enabled, renders the label text in bold to emphasize the section heading. |
-| Default State | Determines whether the accordion should be open or closed when the component is first rendered. This allows for tailored user experiences, especially in forms or detail views. |
+| Setting | Description |
+| :-- | :-- |
+| Label | Heading text shown on the accordion |
+| Bold | Render the label in bold |
+| Default State | Choose whether the accordion starts open or closed |
-## Component support
+## Use it for
-The Accordion component can render nested components within its body:
+* FAQs
+* Settings panels
+* Grouped form sections
+* Supporting content that should stay compact
-* When the accordion is open, all child components are visible.
-* When the accordion is closed, the child content is hidden from view.
+## Structure
-This makes the Accordion ideal for embedding forms, lists, media, or any reusable component inside a collapsible container.
-
-## FAQ example
-
-```Text CSV
-Question,Answer
-"What is your return policy?","We accept returns within 30 days of purchase. Items must be in original condition and accompanied by a receipt."
-"How can I track my order?","After placing an order, you'll receive a tracking number via email once your package ships."
-"Do you offer international shipping?","Yes, we ship to most countries worldwide. Shipping costs and delivery times vary depending on the destination."
-"Can I change or cancel my order?","Orders can be changed or canceled within 2 hours of placing them. Please contact our support team immediately."
-"Are your products eco-friendly?","We prioritize sustainability and use eco-friendly materials whenever possible. Check product pages for more details."
-```
-
-### Component tree
-
-In the component tree, the Accordion acts as a parent container for any content you want to show or hide.
-
-Typical structure:
+The Accordion acts as the parent container for any content you want to show or hide.
1. Add `Accordion` to your screen
-2. Nest content components inside the Accordion body (for example `Text`, `Form`, `Repeater`, or `Container`)
-3. Optionally nest additional components inside those children for more complex layouts
-
-Example tree:
-
-- `Screen`
-- `Accordion (FAQ: Shipping)`
-- `Text` (question/intro)
-- `Container`
-- `Text` (answer)
-
-All nested child components remain in the tree at all times, but their visibility in the app is controlled by whether the Accordion is expanded or collapsed.
-
-
-### Result
+2. Nest content components inside the body
+3. Add more nested components if the content needs further structure
-{`
-
-`}
+When the Accordion is open, child components are visible. When it is closed, they are hidden.
diff --git a/docs/Apps/components/calendar.md b/docs/Apps/components/calendar.md
index baaa28f52..be33b9ce2 100644
--- a/docs/Apps/components/calendar.md
+++ b/docs/Apps/components/calendar.md
@@ -6,314 +6,42 @@ hidden: false
metadata:
robots: index
---
-## Getting Started
+Use the Calendar component to display events on a month, week, day, or agenda view.
-The Calendar component can be used to show events and related data within your datasources. Each event must have three vital pieces of information: Event Start, Event End, and Title. These values will be used to plot events on your calendar.
+## Required fields
-| Property | Type |
-| :---------- | :-------------- |
-| Event Start | DateTime Column |
-| Event End | DateTime Column |
-| Title | String |
+Calendar events need:
-You should first use a Data Provider to fetch your data, and add a Calendar component to your screen, with the datasource set to the Data Provider. Configure your columns for Event Start, Event End, and Title. Your calendar should now display rows from your table. NB: Do not paginate your data provider, and consider how many rows you should display.
+* Event start
+* Event end
+* Title
-
+Use a [Data provider](doc:data-provider) to supply the calendar data.
## Views
-Different views can be selected, depending on your needs.
+* Month view shows a full month grid
+* Week view shows one week at a time
+* Day view shows a single day timeline
+* Agenda view shows a chronological list
-### Month view
+## Common settings
-Month view shows a full month grid. By default, the calendar automatically detects and respects your browser locale to determine the starting day of the week (e.g., Sunday in the US, Monday in the UK). The grid contains day cells containing event summaries.
-Each event entry typically includes a start time (for example `10:45`, `13:00`, `15:00`) and a title (for example `Marketing Call`, `Roadmap Planning`, `Product Sync`).
+* Data provider
+* Event start
+* Event end
+* Event title
+* Type
+* Show title date
+* Title locale
+* Year title format
+* Month title format
+* Day title format
+* Show controls
+* Open on date
-If multiple events occur on the same day, they are listed together in that day’s cell. This makes recurring events and busy dates easy to scan at a glance.
+## On click
-### Week, Day, and Agenda views
+Use calendar click events to read the selected event's row ID and date values.
-- `Week` view focuses on one week and gives more horizontal space per day.
-- `Day` view focuses on a single day timeline for detailed scheduling.
-- `Agenda` view shows events as a chronological list and uses `Empty Agenda text` when no events are available.
-
-## Options
-
-Creators can optionally show or hide the date or date-range being shown in the view. using the "Show title" setting, which can be configured for DD-MM-YYYY or MM-DD-YYYY formats.
-
-Creators also have options to adjust the way the title is displayed, for example in the Title section of the settings, Year title format set to `4-digit`, and `Month title format` set to "Long" will show the title on the Month view as February 2026. Please note that not all settings will apply to all views.
-
-
-
-Creators can also allow their users to move around to different dates or views using the "Show controls" option. By default, the calendar component will open on the current day, but using the "Open on date" setting, and providing a date in YYYY-MM-DD format, the calendar will open on this date (or date range). This could be useful to show specific plans.
-
-## On click events
-
-On-click events can be established to surface the values for an event's `row_id`, event start, event end, and event title. These could be used in conjunction with a Single Row provider in a modal or side panel, or even as part of [Navigate To](https://docs.budibase.com/docs/application-actions#navigate-to) action using [URL Parameters](https://docs.budibase.com/docs/passing-bindings-in-url-parameters)
-
-
-
-
-
-
-
- Setting
-
-
-
- Expected Value
-
-
-
- Use
-
-
-
-
-
-
-
- Data Provider
-
-
-
- Data Provider
-
-
-
- Should fetch a datasource that contains the required columns (detailed below)
-
-
-
-
-
- Event Start
-
-
-
- DateTime column
-
-
-
-
-
-
-
-
-
- Event End
-
-
-
- DateTime column
-
-
-
-
-
-
-
-
-
- Event Title
-
-
-
- Text column
-
-
-
-
-
-
-
-
-
- Type
-
-
-
- Month/Week/Day/Agenda
-
-
-
- Sets which view is shown by default
-
-
-
-
-
- On click event
-
-
-
-
-
-
-
- Read more about [Actions](https://docs.budibase.com/docs/actions)
-
-
-
-
-
- Show title date
-
-
-
- Boolean
-
-
-
- Show or hide the date-range as a title
-
-
-
-
-
- Title locale
-
-
-
- DD MM YYYY or
- MM DD YYYY
-
-
-
- Only visible when "Show title date" is checked.
-
-
-
-
-
- Year title format
-
-
-
- Hidden or 4-digit or 2-digit
-
-
-
-
-
-
-
-
-
- Month title format
-
-
-
- Hidden or Numeric or 2-digit or Short or Long
-
-
-
-
-
-
-
-
-
- Day title format
-
-
-
- Hidden or Numeric or 2-digit
-
-
-
-
-
-
-
-
-
- Weekday title format
-
-
-
- Hidden or Short or Long or Narrow
-
-
-
- Controls formatting of the weekday in the title area.
-
-
-
-
-
- Weekday header format
-
-
-
- Narrow or Short or Long
-
-
-
- Controls formatting of the weekday labels in the table headers.
-
-
-
-
-
- Empty Agenda text
-
-
-
- Text
-
-
-
- Used when the calendar is showing Agenda, with no events to show.
-
-
-
-
-
- Show Controls
-
-
-
- Boolean
-
-
-
- Allow or prevent the user from navigating to different views or dates
-
-
-
-
-
- Button Type
-
-
-
- Action (Default) or Primary
-
-
-
-
-
-
-
-
-
- Open on date
-
-
-
- Text, expects YYYY-MM-DD format
-
-
-
- Calendar will open on current date by default. Set this to open on a specific date.
-
-
-
-
-
-
-
-## Translations
-
-Calendar labels including weekday and month names, as well as button text (e.g. "Today", "Month", "Agenda"), are managed globally. You can customize these in [Workspace Translations](doc:translations) under the **Calendar** category.
\ No newline at end of file
+This is useful for opening a single row provider, side panel, or navigation action.
diff --git a/docs/Apps/components/card.md b/docs/Apps/components/card.md
index 8df451022..99cbbaefa 100644
--- a/docs/Apps/components/card.md
+++ b/docs/Apps/components/card.md
@@ -18,79 +18,52 @@ next:
slug: blocks
title: Blocks
---
-Use the Card component to display lists of data to your users. Cards can contain content, images, and actions, and are a great alternative to the table component; especially, if you want to present an image for each card. You can switch between vertical and horizontal variants of the card using the settings panel.
-
-Cards are highly flexible and have a lot of configuration options. We'll dive into those below
-
+Use the Card component to display records in a compact layout.
> 📘 Cards Block
>
-> Want to quickly convert your data to a list of cards? Use the Cards [Block](doc:blocks) instead
+> Use the Cards [Block](doc:blocks) if you want to render a data source as a card list.
## Layout
-The card component has two different layout modes. Vertical and Horizontal. The vertical mode has every component aligned vertically, whereas the horizontal mode has the image left of the text elements.
-
-For example purposes, I've loaded a list of countries into the database, with flags as images connected to it. In vertical mode, cards render with the image above the text content
-
-### Vertical Mode
-
-
-As you can see, there's 3 items per row. However, this is purely dependent on the screen size. When you change the width of your browser window this can easily change to 1 (narrow or mobile), or 4 (or more) when you make your window wider.
-
-The vertical mode is enabled by default, but in case horizontal mode is enabled, all you have to do is deselect "Horizontal" from the Settings Panel.
-
-The order of elements visible in a card is from top to bottom, Image, Title, Subtitle and Description.
-
-### Horizontal Mode
-
-To enable horizontal mode, you need to check `Horizontal` in the Settings Panel.
+Cards support two layout modes:
+* **Vertical** places the image above the text
+* **Horizontal** places the image to the left of the text
-When horizontal mode is enabled, the image moves to the left of the text content
+Use `Horizontal` in the Settings Panel to switch layout modes.
+## Card elements
-As you can see, the image has shifted to the left of the text elements, where the text elements themselves have not changed.
-
-Quick settings checklist:
-- `Horizontal`: toggles image-above-text (off) vs image-left-of-text (on).
-- `Image URL`: controls the image displayed in the card.
-- `Show button`: toggles the optional button in each card.
-- `Click action`: defines the card click behavior.
-
-## Card Elements
-
-The card consists of several default elements, which are bindable through Handlebars or JavaScript functions. Read more about binding in our [Bindings](doc:bindings) documentation. All elements can be configured in the settings panel.
-
-| Element | Description |
-| :---------- | :--------------------------------------------------------------------------------------------- |
-| Title | The top-most text element inside a card, printed as the largest font and bold |
-| Subtitle | Directly below the title |
-| Description | Below the subtitle, in the smallest font. |
-| Image | The image which can be displayed on top, or on the left of the card (depending on layout mode) |
+| Element | Description |
+| :-- | :-- |
+| Title | Primary label |
+| Subtitle | Secondary label below the title |
+| Description | Supporting text below the subtitle |
+| Image | Optional image shown above or beside the text |
## Button
-Next to the default elements, there's an optional button element. You can enable the button by checking the `Button` in the settings panel
-
-
-The button text can be configured through [Bindings](doc:bindings), or simply text as demonstrated above. The button will then be added to the card as displayed below
-
-
-Right now the button doesn't do anything yet, when you click it, but that can be configured through the `Define Actions` button in the settings panel.
+Cards can also show an optional button. Set the button label with text or a binding, then configure the click action from the settings panel.
> 👍 Define Actions
>
-> To learn more about defining actions, read the [Actions](doc:actions) documentation
+> See [Actions](doc:actions) for how to configure the button click.
+
+## Common use
-## Tutorial
+Cards work well for:
-This tutorial is primarily going to focus on the card component itself as the `Card Block` variant has already been discussed above. This tutorial will be broken down into steps, going from a blank screen to multiple cards and related details shown. Lastly, this tutorial will be using the inbuilt [Budibase DB](doc:budibasedb) as well as the sample data which it provides.
+* Search results
+* Related records
+* Profile summaries
+* Dashboard summaries
-1. Add a data provider component (In this case I'm going to use the Employees table).
-2. Add a repeater component nested inside the data provider.\
-3. Add the card component nested inside the new repeater which you created.\
-4. Bind up all relevant information you wish to display within the card. From the title to the link, or even add a button with relevant actions you want to perform when clicked. For this example I was just show some basic information contained within the employee's table.\
+## Tutorial pattern
-At this point, you should have a data-driven card layout with the desired fields and actions.
+To build a card list from data:
+1. Add a data provider
+2. Add a repeater inside the data provider
+3. Add a card inside the repeater
+4. Bind the title, description, image, and button fields to the row data
diff --git a/docs/Apps/components/container.md b/docs/Apps/components/container.md
index 157ab4102..96a1d1204 100644
--- a/docs/Apps/components/container.md
+++ b/docs/Apps/components/container.md
@@ -10,348 +10,42 @@ metadata:
next:
description: ''
---
-The container now has two different layouts. You can toggle between these two modes by switching between the icons of the layout setting. By default, the grid-based layout is selected. If you wish to use the old layout, you must click the first displayed icon.
+Use the Container component to group content and control layout.
+## Layout modes
-## Grid-based
+Containers support two layout modes:
-This layout allows users to drag, resize, and place their components anywhere on the screen. It also allows you to resize components based on the view mode you have currently selected in the builder. This allows you to build [responsive screens](doc:designing-for-mobile) easily for mobile and tablet devices.
+* **Grid** lets you place and resize components freely in the builder
+* **Flexbox** arranges child components in rows or columns
-To drag, hover over the component and click on its label. This will allow you to drag the component freely within the builder's design area. If you don't click on the label, you will just be able to resize the component.
+Use grid when you want direct placement. Use flexbox when you want predictable responsive layout.
-## Flexbox
+## Flexbox settings
-A Container is a `div` with pre-defined styles. They are the building blocks of your application and critical for responsive design. Containers are used to structure your application and layout other components/elements in a structured way. You can use [flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) to layout the items within your container.
+| Setting | Description |
+| :-- | :-- |
+| Layout | Switch between grid and flexbox |
+| Name | Rename the container in the component tree |
+| Direction | Set child components to row or column |
+| Horizontal align | Align children on the main axis |
+| Vertical align | Align children on the cross axis |
+| Size | Control how the container grows or shrinks |
+| Gap | Set spacing between children |
+| Wrap | Wrap children onto multiple lines |
+| On click | Run actions when the container is clicked |
+## Background colour
-> 📘 Learn Flexbox
->
-> Budibase containers use flexbox to layout components. A great resource for learning flexbox is [flexbox froggy.](https://flexboxfroggy.com/)
+Use the background color setting to separate sections or add emphasis.
-## Container settings
+You can also apply a gradient background. Use the `X` in the color picker to clear a selected color.
-### Flexbox
+## Common use
-There are several general settings for the container component, most of which are shared with other components as well.
+Containers are useful for:
-
-
-
-
-
- Setting
-
-
-
- Description
-
-
-
-
-
-
-
- Layout
-
-
-
- Switch between the flexbox and grid-base layouts.
-
-
-
-
-
- Name
-
-
-
- Change the name of your component, and how it reads within the component tree.
-
-
-
-
-
- Direction
-
-
-
- Change the direction of the components nested within your container. You can layout your components in a row (→) or a column (↓).
-
-
-
-
-
- Horizontal align
-
-
-
- The horizontal. align setting is used to align the items on the main axis, the direction in which flex-direction has set the flow. The initial value is **left** which will line the items up at the start edge of the container, but you could also set the value to **right** to line them up at the end, or **center** to line them up in the center.
-
- You can also use the value **stretch** to take all the spare space between the items, and share it out evenly between the items so there will be an equal amount of space between each item.
-
-
-
-
-
- Vertical align
-
-
-
- The vertical align property will align the items on the cross axis. The initial value is **top** which aligns the items along the top of the container (like they're handing from a roof), but you could also set the value to the **bottom** to align the items along the bottom (like they're standing on the floor), or **middle** to line them up in the center.
-
- You can also use the value to **stretch**, to stretch the items to fill the height of the container.
-
-
-
-
-
- Size
-
-
-
- Where the **grow** option deals with adding space in the main axis, the **shrink** property controls how it is taken away.
-
-
-
-
-
- Gap
-
-
-
- The **gap** property sets the gaps (gutters) between items stored within a container. You can select the different sizes of gaps you would like between your items.
-
-
-
-
-
- Wrap
-
-
-
- Select the **wrap** to wrap your components onto multiple lines. This is critical when designing for mobile resolutions.
-
- With **nowrap**, the items are laid out in a single line which may cause the flex container to overflow.
-
-
-
-
-
- On click
-
-
-
- By defining [actions](https://docs.budibase.com/docs/actions) within the `On Click` setting, the container will be treated like a button including the cursor change. Any click events of buttons or other containers within a container with *On Click* actions set will be ignored.
-
-
-
-
-
-Example setup:
-- `Direction`: `row`
-- `Horiz. align`: `center`
-- `Vert. align`: `top`
-
-This places child components in a horizontal line, centered across the container, aligned to the top edge.
-
-### Container background colour
-
-You can also splash some color into your apps with containers!
-
-
-To clear the selected color, click on the `X` in the bottom right of the panel.
-
-For additional flare, there are a range of gradients available.
-
-
-
-
-
- Title
-
-
-
- Gradient
-
-
-
-
-
-
-
- Warm Flame
-
-
-
-
-
-
-
-
-
- Night Fade
-
-
-
-
-
-
-
-
-
- Spring Warmth
-
-
-
-
-
-
-
-
-
- Sunny Morning
-
-
-
-
-
-
-
-
-
- Winter Neva
-
-
-
-
-
-
-
-
-
- Tempting Azure
-
-
-
-
-
-
-
-
-
- Heavy Rain
-
-
-
-
-
-
-
-
-
- Deep Blue
-
-
-
-
-
-
-
-
-
- Near Moon
-
-
-
-
-
-
-
-
-
- Wild Apple
-
-
-
-
-
-
-
-
-
- Plum Plate
-
-
-
-
-
-
-
-
-
- Peach Kiss
-
-
-
-
-
-
-
-
-
- Flamingo Sunrise
-
-
-
-
-
-
-
-
-
- Bud Mist
-
-
-
-
-
-
-
-
-
- Ballet Slipper
-
-
-
-
-
-
-
-
-
- Black Noir
-
-
-
-
-
-
-
-
-
-You can also add background colors to your [Screens](doc:screens)
-
-
-
-## Component layout
-
-When using containers to layout your components, the most important setting is *Direction* as this will determine the flow of components.
-
-
-
-If you need additional layout you can add additional containers. For example what if we wanted to group 'THREE' and 'FOUR' in column direction, but have an overall row layout for the screen. In this case we need to add a container and nest P3 and P4 like so:
-
-
-Containers are a really flexible way to arrange your components in the **Design** section. You can nest any number of containers with different styles and layout options.
-
-You may, however be interested in [Using sections](doc:using-sections) or [Field groups](doc:field-groups) to get out-of-the-box layouts.
+* Grouping related components
+* Building responsive rows and columns
+* Adding spacing and alignment
+* Creating clickable layout areas
diff --git a/docs/Apps/components/displaying-text.md b/docs/Apps/components/displaying-text.md
index 3f5fc4e0a..330664327 100644
--- a/docs/Apps/components/displaying-text.md
+++ b/docs/Apps/components/displaying-text.md
@@ -1,6 +1,6 @@
---
title: Displaying text
-excerpt: Looking at the different components for display static or dynamic text
+excerpt: Display static or dynamic text.
deprecated: false
hidden: false
metadata:
@@ -10,101 +10,35 @@ metadata:
next:
description: ''
---
-Presenting text content in a structured and readable format is crucial for user experience. Budibase has in the past used several UI components for text, such as Paragraph, Headline, and a Markdown Viewer for displaying text. These three components are combined into one super-flexible "Text" component.
+Use text components when you need to show labels, content, or data-driven values.
-## Text
+## Text component
+Use the Text component for simple text output.
-The Text component is capable of displaying text passed to it in the simplest fashion. However, it also supports [Markdown](https://en.wikipedia.org/wiki/Markdown), making it an exceptionally powerful tool for customising text.
+It supports Markdown and bindings.
-
+## Paragraph and headline
-## Markdown viewer (deprecated)
+Paragraph and Headline were older text components.
-The Markdown viewer component is similar to the paragraph element. It can display text in your application. But the difference is, that it can render markdown. It is really useful to display text coming from any markdown source, including the [Long Form Field](doc:long-form-field) element.
+Use the Text component instead for new work.
+## Markdown viewer
-The *Markdown Viewer* component has full support of all markdown elements as displayed above, including headers, images and tables.
+Use the Markdown viewer when you need to render markdown content from a field or binding.
+## Styling text
-From the Settings Panel you can configure the value of the markdown viewer element, where you can also use [Bindings](doc:bindings).
+Use component settings to control:
-
-
-### Markdown viewer (Deprecated)
-
-The Markdown viewer component is similar to the paragraph element. It can display text in your application. But the difference is, that it can render markdown. It is really useful to display text coming from any markdown source, including the [Long Form Field](doc:long-form-field) element.
-
-
-The *Markdown Viewer* component has full support of all markdown elements as displayed above, including headers, images and tables.
-
-
-From the Settings Panel you can configure the value of the markdown viewer element, where you can also use [Bindings](doc:bindings).
-
-
-
-### Paragraph (Deprecated)
-
-The Paragraph component displays text inside your application. It's the `
` equivalent in `html`. But of course it supports [Bindings](doc:bindings) so you can render any text you like from any of the available Datasource or [JavaScript](doc:javascript) functions.
-
-
-Then, you can use [Bindings](doc:bindings) within the *Text* setting in the Settings Panel, or simply enter static text by just typing in the box, or some combination of both.
-
-For example, you may want to display the total number of rows coming back from a [Data provider](doc:data-provider).
-
-
-Paragraphs also support newlines:
-
-
-
-
-
- Text binding
-
-
-
- Design view
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Customizing text styling
-
-The Paragraph component has several ways of styling the text using the Settings Panel.
-
-| Setting | Description |
-| :-------------------- | :------------------------------------------------------------------------------------- |
-| Size | The size of the text, choose from several presets ranging from `extra small` to `3XL`. |
-| Color | Change the color of the text. Press the `X` on the bottom to reset to default. |
-| Bold/Italic/Underline | Adds the respective bold, italic and underline styling. Can be combined. |
-| Alignment | The alignment of the text. Choose between left, right, center and justify. |
-
-### Headline (deprecated)
-
-The Headline component is identical to the Paragraph component, with the simple caveat that the sizes are three ahead.
-
-For example a *Medium* Headline is the equivalent of a *2XL* Paragraph.
-
-
-
+* Size
+* Color
+* Bold, italic, and underline
+* Alignment
## Related components
-There are a number of other components that can display text, on top of the additional functionality they provide.
-
-These include:
-
* [Button](doc:button)
* [Card](doc:card)
* [Embed](doc:embed)
diff --git a/docs/Apps/components/icon.md b/docs/Apps/components/icon.md
index ffd3ebfe7..ba9a92502 100644
--- a/docs/Apps/components/icon.md
+++ b/docs/Apps/components/icon.md
@@ -12,38 +12,14 @@ next:
---
Icons are scalable vector graphics, which means that they look great on high-resolution displays.
+To add an Icon component, click `Add component` and search for `Icon`, or find it under the *Basic* section.
-To add an Icon component, click `Add component` and search for "Icon", or locate it under the *Basic* section.
-
-
-Within the Settings Panel under the *Icon* setting, a button will show the currently selected icon, or `Pick icon` if no icon has been selected. Click the button to change the icon.
-
-
-This will allow you to filter the Font Awesome library alphabetically or search for keywords.
-
-
+In the Settings Panel, the *Icon* setting shows the selected icon or `Pick icon` if none is set. Click it to change the icon and search the Font Awesome library by keyword or alphabetically.
## Using an icon as a button
-Icons support the **On Click** event. You can define all of the [Actions](doc:actions) that a button can.
-
-
-## Tutorial: Displaying options as icons
-
-In this tutorial, we will create an app with the [sample data](https://docs.budibase.com/docs/data-sources#sample-data), and display an icon based on the *Item Tags* value of the *Inventory* table. To do this, we will make use of [Conditional UI](doc:conditions).
-
-1. Add a [Data provider](doc:data-provider) for the *Inventory* table.
-2. Add a [Repeater](doc:repeater) nested inside the data provider.
-3. Nest an Icon component within the repeater. This will display an icon for each tag.
-
-4. Select the icon component on the far right of the screen. You should see its settings.
-5. Click the conditions tab
-
-6. Click the `no conditions set` button, this will open a drawer were you can configure your conditions
-7. Click the `Add condition` button to create your conditions. In our case, we are going to use the update setting option. This will allow us to dynamically change the icon based on the inventory table's tag values. (Use the duplicate button to make your life easier!)
-
-
-### Result
+Icons support the **On Click** event, so they can trigger the same actions as buttons.
-Using a container to hold the Icon, and a Text component to accompany it, we can then show the item tags and an accompanying icon.
+## Use conditions
+Use [Conditional UI](doc:conditions) to switch the icon based on the current row or component state.
diff --git a/docs/Apps/components/image.md b/docs/Apps/components/image.md
index eb6413095..6184ae779 100644
--- a/docs/Apps/components/image.md
+++ b/docs/Apps/components/image.md
@@ -10,79 +10,26 @@ metadata:
next:
description: ''
---
-The image component is used to display images. To add an image, link to an image using the URL setting. For example, add `https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Photographer_Photographing_Nevada_Mountains.jpg/1920px-Photographer_Photographing_Nevada_Mountains.jpg` to the URL setting, and an image from wikipedia of a photographer photographing mounts will be added to your app.
+Use the Image component to display an image from a URL.
+## Set an image source
-## Displaying images from an attachment
+Add a valid image URL to the component's source setting.
-Images can be uploaded as [Attachments](doc:attachments) and you may wish to display these outside of a [Table](doc:table).
+## Use attachment images
-Images uploaded to either a Single Attachment or Multi Attachment column will both appear as an array of objects. Each object contains specific keys relating to the attachment, including the URL. For single attachment columns like the one used in the Budibase DB sample data table Employees, it is possible to display the image for each employee using `{{ [New Repeater].Employees.[Badge Photo].0.url }}`, assuming you're using a Data Provider and Repeater combo. This takes the current-row from the Data Provider, looks into the Badge Photo column (an array of objects), takes the first (zero-indexed) object, and picks out the URL.
+Use the image bindings from an attachment column when you want to display uploaded files.
-This would be the same as setting the src attribute in an HTML img element
+For single attachments, bind directly to the attachment URL.
-```
-
-```
+For multiple attachments, bind to the attachment array and select the image you want to show.
-
+## Use image backgrounds
+Use a background image component when you want content to appear on top of an image.
-
-
-#### Data
-
-Create a table called _Landscapes_ with a category name and an attachment column for the images. Upload your images.
-
-
-#### Design
-
-1. In an app with a screen, add a [Data Provider](doc:data-provider) for the _Landscapes_ table.
-2. Add a [Repeater](doc:repeater) to the data provider. This will add a container for each _Landscapes_ row.
-3. Add a Text component to display a title for each _Landscapes_ row.
- The Binding in this case is `
- \{\{ Repeater].Lan.Landscapes.title }}
- \{\{ Repeater].Lan.Landscapes.photographer }} \{\{ Repeater].Lan.Landscapes.date }}
- ` which will fetch and display a title, the name of the photographer, and the date saved in the table.
-4. Underneath the Text, add a data provider for the "Images". Attachments List column types which we created are saved in JSON Arrays.
-5. Change the source of the data provider to JSON / CSV
-
-6. Click the settings cog beside JSON / CSV and paste this binding inside it `{{ [New Repeater].Landscapes.images }}`
-7. Add a repeater so that each attachment image can be displayed.
-8. Finally, add an Image component within the repeater. Paste the below binding inside the URL setting of the image component. Using JavaScript `return $("[New Data Provider 2].Rows")[$("[New Repeater 2].[Row index]")].url` - This sets the image URL using the second Data Provider and the repeater's row-index.
-
-#### Result
-
-
-#### Single or fixed number of image attachments
-
-If you know there will only be a single image attachment, you should make sure to use the [Attachment](doc:attachments#attachment) column type. Then you do not need to add a data provider and repeater for the attachment column. You will have access to the image bindings.
-
-For a fixed number of attachments, simply adjust the index in the URL for each image component. This was shown in the above example
-
-## Displaying images from an S3 bucket
-
-You may wish to store your image files in an external S3 bucket instead of the built-in MinIO bucket.
-
-#### Data
-
-Upload your images to your S3 bucket. You will need to allow public access to the images in AWS.
-
-Next add your S3 Datasource with a simple **Read** query to list all of the files in the bucket.
-
-#### Design
-
-Add a [Repeater Block](https://docs.budibase.com/docs/blocks#repeater-block) with the _Read_ query as its datasource. Nest your image components, using the following URL binding: `https://.s3.us-west-1.amazonaws.com/{{ New Repeater block.Read.Key }}`
-
-
-## Image carousel
-
-There is no built-in slideshow component, however there is a tutorial here for building one.
-
-## Background image
-
-Add the background image component. This component can take children, which will appear in front of the background.
-
-
-Provide a valid URL as the image source. The _Position_ property can also be edited to offset the background image.
+## Related pages
+* [Attachments](doc:attachments)
+* [Data provider](doc:data-provider)
+* [Repeater](doc:repeater)
diff --git a/docs/Apps/components/index.md b/docs/Apps/components/index.md
index 3ab81afc6..5df872662 100644
--- a/docs/Apps/components/index.md
+++ b/docs/Apps/components/index.md
@@ -10,88 +10,77 @@ metadata:
next:
description: ''
---
-Components are the UI elements of your Budibase apps. They are used to construct your user interface, which could be components that your user will interact with, or components for layout and displaying data. At Budibase we use the Spectrum design system created by Adobe. This gives you a great out-of-the-box experience with themes by default.
+Components are the building blocks of Budibase apps. Use them to collect input, display data, and shape screen layout.
-***
+## Add components
-## Adding components
+Add components in one of two ways:
-There are a few different ways to add a component. If you have no components added, in the middle of your screen, there will be some text as well as a button that will allow you to add a component. The main way to add a component is to click the plus symbol found at the bottom right of your screen.
+1. Use the add component button in the builder.
+2. Select a component type from the searchable component list.
-Clicking any of the above options will present a searchable and grouped list of components in the right-hand panel.
+When the component list is open, press `Enter` to add the highlighted component.
+## Delete components
+Select a component and press `Backspace` or `Delete`.
-When you search for a component, if it is highlighted in blue, add it immediately by pressing the *Enter* key.
-
-## Deleting a component
-
-The easiest way to delete a component is to select it and press the **delete** or **backspace** key.\
-You will be prompted to confirm the deletion.
-
-Alternatively, you can click on the three dots `...` and then click the delete action inside the opened context menu.
-
-
-
+You can also delete a component from its menu.
## Component tree
-The component tree displays the layout and hierarchy of your components.\
-Components can either be below one another, or nested inside a parent component.
+The component tree shows the structure of the screen.
-Parent components include those related to a Datasource or some variety of container:
+Use it to:
-* [Data Provider](doc:data-provider)
-* [Repeater](doc:repeater)
-* [Forms](doc:forms), [Form steps](doc:form-steps) and [Field groups](doc:field-groups)
-* [Container](doc:container)
-* [Sections](doc:using-sections)
-* [Side Panel](doc:side-panel)
-* Modal
-* [Field Group](doc:field-groups)
+* Reorder components with drag and drop
+* Nest components inside containers, forms, repeaters, or other parent components
+* Copy, move, or delete components from the `...` menu
-Components can be re-arranged within the component tree with drag-and-drop.\
-To assist with rearrangement, a green line will highlight the destination whilst a green box indicates that a component will be dropped inside that component.
-
-### Searching components
-
-Click the search icon beside Screen in the Components panel to open the search field. You can search by a component’s name or type. Matching components are highlighted, and the tree automatically expands to reveal them.
-
-#### Editing
-
-Within the component tree you can also delete, copy and move components by clicking on the `...` beside the component name.
+Common parent components include:
+* `Data Provider`
+* `Repeater`
+* `Form`
+* `Form step`
+* `Field Group`
+* `Container`
+* `Section`
+* `Side Panel`
+* `Modal`
## Keyboard shortcuts
-The following keyboard shortcuts are available for components. You can replace the `⌘` (command) button on Mac with `CTRL` on Windows if you wish to use the shortcuts on Windows.
-
-* **Delete** - Backspace `⌫` - Allows quicker deletion of a selected component
-* **Move up** - `⌘` + `↑` - Allows the user to move a component up in the component tree
-* **Move down** - `⌘` + `↓` - Allows the user to move a component down in the component tree
-* **Duplicate** - `⌘` + `D` - Duplicate component with all settings added
-* **Cut** - `⌘` + `X` - Removes the component, saves it for pasting elsewhere
-* **Copy** - `⌘` + `C` - Copies a component and allows it to be pasted elsewhere, e.g. a new screen
-* **Paste** - `⌘` + `V` - Pastes a copied/cut component in the component tree
+Common shortcuts include:
-There are a range of other ways to speed up navigate or perform additional actions more quickly. This is mainly done via the [Command Interface](doc:command-interface)
+* `Backspace` or `Delete` to remove a selected component
+* `⌘ + ↑` to move a component up
+* `⌘ + ↓` to move a component down
+* `⌘ + D` to duplicate a component
+* `⌘ + X` to cut a component
+* `⌘ + C` to copy a component
+* `⌘ + V` to paste a component
-## Renaming components
+Use `Ctrl` instead of `⌘` on Windows.
-Sometimes, it's helpful to organise your component tree in a way that clearly reflects the purpose of each component. For instance, you might want to differentiate between various forms on a screen, such as the `Create Users form` or the `Edit Users` form. Similarly, labelling buttons can clarify their action, whether they're for endorsing, deactivating, etc.
+## Rename components
-To rename a component, first select it. Then, locate the component's settings at the top in the side panel. Click the current name, delete it, and enter your desired name. Use these steps to rename components consistently across your app.
+Rename components to make the tree easier to scan.
+1. Select the component.
+2. Open its settings in the right-hand panel.
+3. Replace the current name with a clear label.
-## Using components
+Use names that describe the component’s purpose, not just its type.
-When you have added a component you will be able to see the options that you can tweak on the right side under the Settings bar. Some components also have a toolbar that will appear above it in the preview window.
+## Use components
-To find and rename a component quickly: select it in the canvas or component tree, then use the right-hand Settings panel where the component name appears at the top.
+Once a component is on the screen, its settings appear in the right-hand panel.
+Some components also expose controls in the canvas preview. Use those when you need to adjust layout or content without changing the component tree.
-## Video tutorial
+## Related guides
-{`
-
-`}
+* [Command interface](doc:command-interface)
+* [Screens](doc:screens)
+* [Using data providers](doc:data-provider)
diff --git a/docs/Apps/components/tag.md b/docs/Apps/components/tag.md
index ffd1488bd..bd7c3d393 100644
--- a/docs/Apps/components/tag.md
+++ b/docs/Apps/components/tag.md
@@ -10,177 +10,36 @@ metadata:
next:
description: ''
---
-The `Tag` component can quickly display tags on, for example, a detail page. An optional delete button can be added, which can be configured using [Actions](doc:actions).
+Use the `Tag` component for short labels such as statuses, categories, and related values.
+## Customise the tag
-## Customising the tag
+| Setting | Description |
+| :-- | :-- |
+| Size | Set the text size |
+| Color | Set the tag color |
+| Allow delete | Show an `X` delete control |
+| On click delete | Configure what happens when the delete control is clicked |
-To customise the tag, you have several settings in the Settings Panel you can use.
+## Common use
-| Setting | Description |
-| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Size | The size of text, choose between small, medium or large. |
-| Color | Choose the color of the button, the text color will change accordingly. |
-| Allow delete | Adds an `X` delete control to the tag. |
-| On click delete | This setting appears when `Allow delete` is checked. Use it to configure what happens when the user clicks the `X`, using [Actions](doc:actions). |
+Tags work best when the value is short and repeatable.
-
+Common examples include:
-## Tutorial: Movie genre tags
+* Record statuses
+* Category labels
+* Related items in a repeater
+* Filter chips in compact views
-In this tutorial we will look at how you can use the tag component to display movie genres.
+## Tutorial pattern
-### Setup
+Use a repeater when the tag values come from related data:
-To get started, add an internal [Budibase DB](doc:budibasedb) table called *Movies*, with 'Title', 'Year', and 'Image Url' columns. Add some rows.
+1. Build the parent list or detail view
+2. Add a repeater for the related items
+3. Add the Tag component inside the repeater
+4. Bind the text to the related field
+5. Use [Conditional UI](doc:conditions) to vary the color when needed
-
-Next go to the *Design* section, and add a [Cards block](https://docs.budibase.com/docs/blocks#cards-block). Select the *Movies* table as the Datasource, and bind the *Title*, *Subtitle* and *Image URL* values to the table columns. For example, the *Subtitle*: `{{ New Cards block.Movies.Year }}`
-
-Your screen should now show cards populated from your `Movies` data.
-
-
-### Adding the tags
-
-Go back to the *Data* section, and add another table called *Tags*. Give it a text column called 'Name' and a Many->Many [Relationship](doc:relationships) column linking to *Movies*.
-
-
-Add some tag rows, linking them to some movies.
-
-
-Head back over to the *Design* section. To be able to add our tags, we must [Eject](https://docs.budibase.com/docs/blocks#ejecting-blocks) the cards block.
-
-
-Next add a [Container](doc:container) nested under the *Repeater*. Set the *Gap* to 'None'. Drag the *Spectrumcard* into the new container.
-
-Below the *Spectrumcard* insert a [Repeater block](https://docs.budibase.com/docs/blocks#repeater-block). Give it the name 'Tags Repeater Block', and select the *Tags* table from the relationship.
-
-
-Finally add the Tag component underneath the *Tags Repeater Block*. Click *Align right* on the repeaters block to align the tags to the right-hand side of the card, and choose *Row layout*.
-
-Give the tag the text binding: `{{ Tags Repeater block.Tags.Name }}`. This can be selected from the bindings dropdown.
-
-
-As a finishing touch, it would be nice to give the different tags a color. To do so, we will use [Conditional UI](doc:conditions). Click on `Configure conditions`: Update Setting 'Color' TO ... IF `{{ Tags Repeater block.Tags.Name }}` Equals ...
-
-The duplicate condition button will be very useful in this case!
-
-
-#### Result
-
-At this stage, each movie card should display its related tags directly beneath the movie details.
-
-For example:
-- **The Shawshank Redemption (1994)** with tags like `Drama`, `Crime`
-- **Pulp Fiction (1994)** with tags like `Action`
-- **The Godfather (1972)** with tags like `Crime`, `Action`
-- **Inception (2010)** with tags like `Sci-Fi`, `Drama`
-
-This confirms the relationship binding is working and that tags are being rendered per movie row in the repeater.
-
-To see how tags can be used in a SQL database, have a look at this [MS SQL Server tutorial](https://docs.budibase.com/docs/ms-sql-server).
-
-### Removing tags
-
-You may optionally also want the ability to remove tags. To do so, first check the `Allow delete` setting. This will allow you to specify some actions.
-
-Quick flow:
-1. Enable `Allow delete` on the Tag component.
-2. Configure an action under `On click delete`.
-3. When users click the `X`, your configured action runs and the tag is removed according to that logic.
-
-Add a **Save Row** action for the `On click delete` setting with the *Movies* Repeater as the datasource (not the tags repeater block), and select the *Movies* table as the save destination.
-
-Check *Do not display default notification* so that a toast is not displayed. Also `Add column` for the *Tags* relationship and give it the following [JavaScript](doc:javascript) binding:
-
-```javascript
-return $("Repeater.Movies.Tags")?.filter(tag => tag._id !== $("Tags Repeater block.Tags._id"));
-```
-
-The JavaScript binding may appear a little daunting to some, but to explain in pseudo-code:
-
-> Save all of the movie tags excluding the one we are removing
-
-After removing all tags, you will see a *No rows found* message, which isn't what we'd like in this case.
-
-
-Ideally this should just be blank. Click on the *Tags Repeater block* and `Configure conditions`: Hide component IF `{{ Repeater.Movies.Tags }}` Is empty.
-
-As a finishing touch, it would be nice to add some margin when there are no tags so that the vertical spacing is consistent. Click on the *Spectrumcard* and `Configure conditions`: Update Setting 'Custom CSS' TO `margin-bottom: 32px;` IF `{{ Repeater.Movies.Tags }}` Is empty.
-
-#### Naive result
-
-
-As you probably noticed, there is substantial delay between clicking the 'x' and the tag being removed. Ideally in this scenario we don't want to await for a database response. We can use [App state](doc:app-state) to cache the tags and improve the tag deletion UX.
-
-Add a second action for updating app state:
-
-In the Actions drawer:
-1. Click `Add Action`.
-2. Choose `Update State`.
-3. Set `Type` to `Set value`.
-4. Set `Key` to `RemovedTags`.
-5. Set `Value` to a JavaScript function (shown below).
-6. Leave `Persist this value` unchecked unless you explicitly want removed-tag state to survive page reloads/browser restarts.
-7. Click `Save`.
-
-
-```javascript
-return {
- ...$("State.RemovedTags"),
- [$("Tags Repeater block.Tags._id")+$("Repeater.Movies._id")]: true
-}
-```
-
-Next we want to replace the **Save Row** action with a **Trigger Automation** action instead. Follow the below steps to do this.
-
-Create the automation
-1. Switch to the automation section
-2. Create a new on demand automation
-3. Add the relevant fields
- - tags
- - movie_id
-
-In the Actions drawer:
-1. Keep your first action as `Update State`
-2. Add a second action: `Trigger Automation`
-3. Under `Automation`, choose previously created on demand trigger automation, and keep `Do not display default notification` checked
-4. Leave `Require confirmation` unchecked unless you want a user confirmation prompt
-5. Add fields:
- - `tags` -> `Value`: JavaScript function (shown below)
- - `movie_id` -> `Value`: `{{ Repeater.Movies._id }}`
-6. Click `Save`
-
-`Add Field` for the *tags*, and *movie\_id*.
-
-The JavaScript binding for the *tags* field looks as follows:
-
-```javascript tags
-return $("Repeater.Movies.Tags")?.filter(tag => !$("State.RemovedTags")?.[tag._id+$("Repeater.Movies._id")]);
-```
-
-Here we are only looking to save tags for each movie that have not been flagged as removed in our front-end cache.
-
-Instead of waiting for a save row response, we will fire and forget a request to our [Automation](doc:contextual-bindings) which will remove the chosen tag from our chosen movie.
-
-After saving those actions, navigate to the *Automate* section and select the **DeleteTag** automation that was automatically created for us.
-
-Make sure *tags* is set to type **Array**.
-
-
-Next add an **Update Row** step for the *Movies* table as follows, making sure the `Use bindings` options is selected!
-
-In addition, check the `Clear relationships if empty?` flag to ensure that all tags can be removed.
-
-
-> 👍
->
-> Publish your app to activate the automation.
-
-Navigating back to the *Design* section, add another condition to the tag: Hide component IF
-
-```javascript
-return $("State.RemovedTags")?.[$("Tags Repeater block.Tags._id")+$("Repeater.Movies._id")];
-```
-
-Equals Boolean True.
+If you need delete support, enable `Allow delete` and wire the delete control to an action.
diff --git a/docs/Apps/components/text-inputs.md b/docs/Apps/components/text-inputs.md
index 462fe2150..32598f61c 100644
--- a/docs/Apps/components/text-inputs.md
+++ b/docs/Apps/components/text-inputs.md
@@ -1,6 +1,6 @@
---
title: Text inputs
-excerpt: Form fields that require key-press input
+excerpt: Form fields that accept typed input.
deprecated: false
hidden: false
metadata:
@@ -10,133 +10,48 @@ metadata:
next:
description: ''
---
-In Budibase several fields require user key-press input. They are:
+Use text inputs when users need to type a value into a form.
-* JSON field
-* Long form field
-* URL field
+Common text input types include:
+
+* Text field
* Number field
* Password field
-* Text field
+* URL field
+* Long form field
+* JSON field
* BigInt field
-## Adding text inputs
-
-In all cases these components must be nested within a [Form](doc:forms) component.
-
-Within the component panel of your screen click `Add component` and select the Form component.
-
-Next click on the form component that was just added, then click add component again. This will ensure that the field will be nested inside the form once added, however you can also drag orphan input fields into your form if needed.
-
-When looking for a key-press input, a handy tip is to search for the word 'field'.
-
-
+## Add a text input
-Finally, for an input to be useable, it must be given a field name. If the form has been given a [schema](https://docs.budibase.com/docs/forms#form-schema), you will be able to select from a list of type-matched fields. Alternatively, you can manually enter a field name.
-
-
-
-## Using form blocks
-
-When using a [Form block](doc:form-block) the field components are not directly accessible. If you want to edit the field components, then you will need to [Eject](https://docs.budibase.com/docs/blocks#ejecting-blocks) the form block.
-
-Otherwise, Budibase will match the [Data type](https://docs.budibase.com/docs/budibasedb#data-types) of the field to a corresponding field component. Most commonly this will be the **Text field** and **Number field**, however it could also be the **JSON field** for the [JSON](doc:json) data type, among others. The **URL field** is a specialised text input for website addresses and is especially useful when you want built-in URL autocomplete and validation.
+1. Add a [Form](doc:forms)
+2. Add the text input inside the form
+3. Bind the field to a form schema field or enter a custom field name
## Common settings
-All of the key-press inputs share common settings, which are presented in the reference table below.
-
-| Setting | Description |
-| :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Field | Connected field from the parent [Form](doc:forms) element, or custom by typing in a field name. |
-| Label | The text visible to the user next to the input box as a descriptor of the field. |
-| Placeholder | The text displayed in a lighter tint inside the input box. Will only display when nothing is entered in the field. |
-| Default Value | On initial load of the form the field will be pre-filled with specified default value. Entering this will also prevent the `placeholder` from showing. |
-| Disabled | When checked, disables the field; the user cannot change the value. |
-
-### On change
-
-Allows you to specify [Actions](doc:actions) to be triggered when the input field changes value. Specifically this event is triggered when the user presses the tab key or clicks off the field (on blur), or if the user pressed the enter key.
-
-When using the field value as a Binding, make sure to use the current field value. For example, you may want to use the first name value in an on change action:
-
-
-
-> 👍 `{{ Field Value }}`
->
-> Gets the current field value.
-
-> ❗️ `{{ Form.Fields.First Name }}`
->
-> Will get the previous value; before the on change was triggered.
-
-
-
-### Run on input
-
-Triggers the same Actions on input events (_typing, paste, barcode scans_) as the value changes, rather than waiting for the change event. Available for Text, Number, BigInt, Password, and Image fields where an input event is emitted.
-
-
-
-
-### Configure validation
-
-Allows you to configure custom validation rules for the input field.
-See more about [Form validation](doc:form-validation).
-
-## Text field
-
-The text field allows users to enter inline text. This is the most common text input.
-
-It is compatible with the [Text](doc:text) data type.
-
-## URL field
-
-The URL field allows users to enter website URLs.
-
-It is compatible with the [Text](doc:text) data type, and is useful when you want browser URL autocomplete plus validation for web addresses.
-
-By default, Budibase validates `http` and `https` URLs in this field. You can broaden the validation rule to allow other protocols if required.
-
-
-
-## Number field
-
-Allows users to enter whole or decimal numbers.
-
-It is compatible with the [Number](doc:number) data type.
-
-
-## Password field
-
-Same as the text field, however it hides the text being entered.
-
-Any binding of the field value will be in plain text, and Budibase does not perform any hashing by default on password fields when saving rows.
-
-
-## Long form field
-
-The long form field is a text field that allows for longer and multiline input.
-
-
-You can also enable rich-text editing with markdown support by selecting `Rich text` in the formatting setting.
-
-
-
-Usually when [importing a table from CSV](https://docs.budibase.com/docs/budibasedb#csv-upload) or [fetching SQL tables](https://docs.budibase.com/docs/sql-datasource#fetching-tables) string data types will automatically be assigned as a Text type.
-
-If you want to support the long form field, then you will need to [edit column](https://docs.budibase.com/docs/budibasedb#editing-columns) and change the type.
-
+* Field
+* Label
+* Placeholder
+* Default value
+* Disabled
-## JSON field
+## On change
-The JSON field allows you to enter [JSON](doc:json) into resizable box with formatting enabled.
+Use `On change` when the action should run after the user leaves the field or presses Enter.
+Use `{{ Field Value }}` to reference the current value.
-## BigInt field
+## Run on input
-This is a data type in databases used to store very large integer values. It is commonly used when the range of values needed exceeds the limits of the standard INT data type.
+Use `Run on input` when the action should fire while the user is typing or pasting.
-In most systems, an INT field typically supports values from -2,147,483,648 to 2,147,483,647, but a BIGINT field can handle a much larger range. It ensures you can store these values without overflow errors.
+## Field types
-This is useful when working with large datasets e.g. financial records, large IDs, or when performing mathematical computations that result in large numbers.
+* Text field for inline text
+* URL field for web addresses
+* Number field for numeric values
+* Password field for hidden text
+* Long form field for multiline input
+* JSON field for JSON data
+* BigInt field for very large integers
diff --git a/docs/Apps/conditions.md b/docs/Apps/conditions.md
index 6c1faeeea..41c04e5b4 100644
--- a/docs/Apps/conditions.md
+++ b/docs/Apps/conditions.md
@@ -10,59 +10,38 @@ metadata:
next:
description: ''
---
-A lot of the time you want to change the state of some component when certain conditions are true or false. This might be changing color from red to green, showing or hiding a component. You will find the condition settings at the very bottom of the settings panel of your components.
+Use conditional UI when a component should change based on a value or piece of logic.
-Adding a condition is simple. Once you click the settings button a drawer will pop up and you will be able to add, edit or remove your conditions. It will look something like this
+Common uses include showing, hiding, disabling, or updating a setting.
-
+## Add a condition
-### Enabling and disabling conditions
+1. Select a component
+2. Open the `Conditions` section in the settings panel
+3. Select `Add condition`
+4. Choose the action you want to apply
+5. Define the comparison
-You can temporarily disable a condition without deleting it by using the toggle switch located next to each condition in the drawer. This is useful for testing different UI states or temporarily pausing logic during development. Disabled conditions are ignored when evaluating component settings or visibility in both the builder preview and the published app.
+## Condition actions
-When conditions are configured, the **Conditions** tab in the component settings panel displays a status indicator dot:
-* **Green**: All conditions are enabled.
-* **Orange**: Some conditions are disabled.
-* **Red**: All conditions are disabled.
+* Show
+* Hide
+* Update setting
-## Adding a condition
+## Enable or disable a condition
-To add a condition, simply click the `Add condition` button. As explained above, you can choose whether to `Show`, `Hide` or `Update setting`. Depending on what you choose you will be presented with different options. They will all contain settings for comparing some value against another.
+Use the toggle in the conditions drawer to disable a condition without deleting it.
-.png")
+Disabled conditions are ignored in the builder preview and the published app.
-> 👍 Show/Hide
->
-> When using a Show on boolean condition, the Hide occurs automatically for the opposite case, and vice versa.
+## Use role-based conditions
-## Tutorial
+Conditions can also hide or show components based on the current user's role.
-In the example we're going to discuss we have a simple backend table that contains three records. It looks like this:
+Role values must be uppercase:
-
+* `BASIC`
+* `POWER` (Deprecated)
+* `ADMIN`
-As we can see, there are four fields, Company, Due Data, Value, and Paid. In the Design section, we have designed a simple interface showing each of our invoices in rows. It looks like so:
-
-
-
-It would be great if the `Pay Invoice` button was in a disabled state if the invoice has been paid. So let's go ahead and add a condition to do just that!
-
-Select the button component in the tree view on the left. Open the conditions drawer and select ``Update setting` and then pick the Disabled setting in the dropdown. Make sure the checkbox is checked. We then need to figure out which field we want to check. In our case, it's found under `{{ Repeater.Invoices.Paid }}`. This will of course differ depending on the structure of your application. If you click the little lightning bolt icon you will be able to see which fields are available for use. Now we just make sure that it equals TRUE. Hit save, and that's it!
-
-
-
-Now that we have this condition set up we might also want to change the text of the button to say "PAID". To do that, simply click the copy button and change the setting to "text". Change the text to "PAID" and hit save.
-
-And here's the end result.
-
-
-
-## Showing and hiding a component based on the user's role
-
-When building apps, we often want to limit a user's access to specific components. For example, we may not want a user to see certain data or access a particular action.
-
-Below is a quick demonstration of how to hide a button if the user's role is BASIC. Please remember, when typing the value of the user role, it must be in capitals: `BASIC`, `POWER`, `ADMIN`.
-
-{`
-
-`}
\ No newline at end of file
+NB: Custom Roles must be referenced by their ID, not their name.
diff --git a/docs/Apps/custom-css.md b/docs/Apps/custom-css.md
index 0883e687e..b2465bbdd 100644
--- a/docs/Apps/custom-css.md
+++ b/docs/Apps/custom-css.md
@@ -10,58 +10,31 @@ metadata:
next:
description: ''
---
-All [Components](doc:components), including [Screens](doc:screens), have the option to add custom css.
+Use custom CSS when you need to override the default styling of a component.
-To do so, click on a component, and in the Settings Panel click on the *Styles* tab. Finally click on the *Edit custom CSS* button
+## Add custom CSS to a component
-
+1. Select the component
+2. Open the `Styles` tab in the settings panel
+3. Select `Edit custom CSS`
+4. Enter inline CSS for the component
-This will present a box in which you can enter CSS for the selected component.\
-For example:
+Custom CSS here applies only to that component.
-
+Selectors are not supported in this field.
-> 🚧 Inline CSS
->
-> Selectors cannot be used here, and you must enter inline CSS.
+## Apply global styles
-## Global styling
+Use an [Embed](doc:embed) component when you need page-wide CSS selectors.
-If you are determined to apply your own style to the entire screen, you can achieve this through an [Embed](doc:embed) component.
+Add a `
-`}
+Use synchronous automations when the caller needs data from the automation run.
-Synchronous automations allow users to receive feedback from their automation, in the Design section or via a webhook response.
-
-Automations are usually asynchronous, this means that when an automation runs, it does so in the background and the user is unable to access its result, but with synchronous automations the result is available to the user.
+Most automations run asynchronously. A synchronous automation returns data to the caller.
> 📘 Licensing🔒
>
-> The following features are available to:
->
-> * **All installations** with a Premium or Enterprise license
-
-
-
-## Creating a Synchronous Automation
-
-You can make an automation synchronous by adding a **Collect Data** step.
-
-
-
-A **Collect** step will simply return whatever binding or value is passed to it.
-
-Once this step has been added, you can access the result of your automation as a Budibase user, or external user depending on whether your automation trigger is [On demand](doc:app-action) or [Webhook](doc:webhook).
-
-> 📘 Last step
->
-> As the **Collect Data** action returns data from your automation, it must be the last step. Otherwise you will see that the action is greyed out:
->
->
-
-
-
-## Webhook
-
-When using the [Webhook](doc:webhook) trigger, you can return data to the caller of the trigger URL.
-
-For example, consider this webhook automation that queries an Employees table, filtering on the *city* value that is passed into the trigger:
-
-
-
-
+> This feature is available on Premium and Enterprise plans.
-We can then specify data that should be returned to the caller of the trigger, in this case simply all of the rows matching the city provided.
+## Create a synchronous automation
-
+Add a `Collect Data` step to make an automation synchronous.
-After publishing this automation, we can see the result in our POST request:
+The `Collect Data` step returns the binding or value passed into it.
-
+The step must be the last step in the automation.
-You can now see the response from the **Collect Data** step has been provided in the response of the trigger call. Note that value in this case is a string - this is because **Handlebars always return strings.**
+## Webhook use case
-If you want to ensure that the correct typing is returned, such as for numbers and arrays, then you can use a JavaScript binding instead.
+Use a [Webhook](doc:webhook) trigger when an external service needs to call the automation and receive data back.
-
- Making sure an array is returned
-
+The response can include the value returned by the `Collect Data` step.
-And now the result:
+If you need a number or array, use a JavaScript binding instead of a Handlebars value.
-
+## On demand use case
-
+Use an [On demand](doc:app-action) trigger when a screen action needs to receive the automation result.
-## On demand
+Typical flow:
-For users to access the result of your automations in a published Budibase app, you will need to configure an [On demand](doc:app-action) trigger within the *Design* section. Let's start off by creating a very simple automation that will get a specific row based on the \_id we are passing to it.
+1. Trigger the automation from a button
+2. Store the result in app state
+3. Bind the state value to a component
-
+## Notes
-
- Filter attached to the query rows step
-
-
-With this done we can start setting up our screen. Create a new screen and add a button component. Select the component. The button's settings side panel should show up on the right of your screen. Then click on the button beside the label `On click` called `No actions set`. This will open the actions drawer. Add a new trigger automation action and select our newly created On demand automation.
-
-
-
-> 🚧 Timeout
->
-> As shown in the screenshot, a synchronous automation can run for up to 120 seconds, or less if required.
-
-Because we have a collect step at the end of the automation, a binding with the result of the automation can be provided to any of the following actions, through [Chaining actions](doc:chaining-actions).
-
-In this example, we will use the binding to add the result of an automation into [App state](doc:app-state) and then display it in a paragraph component.
-
-
-
-When the button with these actions attached is clicked, state will now be updated with a key called `result` that contains the result of our automation.
-
-To display this we now simply to need bind the state to the Text component.
-
-
-
-We can now preview our app and click the [Button](doc:button) for which our actions are defined.
-
-
-
-The automation result is stored in state, and due to the binding of that state to the text component, appears on screen whenever the `Trigger` button is clicked.
-
-Instead of displaying the result in a component, you could just as easily use the **Show Notification** action, or any other combination of actions.
-
-> 📘 Automation Result
->
-> The automation result binding will only be available if your automation has a **Collect Data** step.
\ No newline at end of file
+* Synchronous automations can run for up to 120 seconds
+* The automation result binding is only available when the automation includes `Collect Data`
+* Use chaining actions to pass the result into later steps
diff --git a/docs/Automation/trigger/app-action.md b/docs/Automation/trigger/app-action.md
index 0f588be6a..e615d631f 100644
--- a/docs/Automation/trigger/app-action.md
+++ b/docs/Automation/trigger/app-action.md
@@ -12,13 +12,13 @@ metadata:
next:
description: ''
---
-The On demand trigger runs when invoked from the UI of your Budibase application, or by an agent workflow.
+Use the On demand trigger when you want an app action or agent workflow to start an automation.
> 📘 Fire and forget
>
-> Automations run asynchronously on a background thread; therefore, once an automation is triggered from the client, there will be no returned response by default.
+> On demand automations run asynchronously. By default, they do not return a response to the client.
>
-> To receive a response, you can make use of [Synchronous automations 🔒](doc:synchronous-automations).
+> If you need a response, use [Synchronous automations 🔒](doc:synchronous-automations).
## Creating the Automation
@@ -26,36 +26,36 @@ Create a new automation in the Automation section, choose `On demand` as the tri
## Defining Fields
-Click the trigger step to open the right-hand panel and define your fields. These fields are the input data passed into your automation for use in later steps.
+Click the trigger step to open the right-hand panel and define your fields. These fields are the input data passed into your automation for later steps.
Supported field types are `Text`, `Number`, `DateTime`, `Boolean`, and `Array`.
-You can use these fields in the next steps, check the [Working with data in automations guide](doc:data-in-automations) to learn how.
+You can use these fields in later steps. For more detail, see [Working with data in automations](doc:data-in-automations).
## Populating Fields from the UI
-Now that you have defined fields to be used in your automation, you can call this automation from the UI and populate the fields you just selected.
+Now that the fields are defined, you can call the automation from the UI and populate those values.
Add a button in the Design section, then open its `No actions set` option in the Settings Panel.
-A modal will open where you can define [Actions](doc:actions). Create a new action by choosing `Trigger Automation` from the options and clicking the `Add Action` button.
+A modal opens where you can define [Actions](doc:actions). Create a new action by choosing `Trigger Automation` and clicking `Add Action`.
-Then select your On demand automation from the dropdown list. Only On demand automations will appear in this list.
+Then select your On demand automation from the dropdown list. Only On demand automations appear here.
-After selecting your On demand automation and any fields you created, they will be pulled through below the select automation. You can freely type the values you wish to pass. Or you can use any available bindings and pass those to the automation instead.
+After selecting the automation and fields, they appear below the selector. You can enter static values or use bindings instead.
-After selecting the field you want to populate, you can enter the value. This can be static text, or through [Bindings](doc:bindings). You could even use [JavaScript Bindings](doc:javascript) for it.
+After selecting a field, enter the value. This can be static text, a [Binding](doc:bindings), or a [JavaScript Binding](doc:javascript).
-Whatever you choose for bindings, you will receive the result back in your automation, which you can then use in the next steps.
+Whatever values you choose, the automation can use them in later steps.
## Using Trigger Fields
-This subject touches upon [working with data in automations](doc:data-in-automations), but I wanted to highlight the specific data that comes in with this trigger.
+This section focuses on the data that comes in with the trigger. For related guidance, see [Working with data in automations](doc:data-in-automations).
-One popular thing to do with an `On demand` trigger is creating one or multiple database rows. You could use this action to populate various tables, or even Data Sources at once.
+One common use for an `On demand` trigger is creating one or more database rows. You can use it to populate tables or even Data Sources at once.
-Let's take a look at the `Create Row` action. Click the step to open its settings, select your table, and use [Bindings](doc:bindings) with `trigger.fields` to map incoming values.
+The `Create Row` action is a good example. Click the step to open its settings, select your table, and use [Bindings](doc:bindings) with `trigger.fields` to map incoming values.
-As you can see, we're using the same names for the fields we defined in the first step.
+Use the same field names you defined in the trigger step.
-And if you want, at this stage, you could create another `Create Row`, or another action to create a row in another table, or somewhere else entirely. This could also be a good moment to duplicate data to [MongoDB](doc:mongodb).
\ No newline at end of file
+You can also add another `Create Row` action, or send data to another table or data source. This is also a good place to duplicate data to [MongoDB](doc:mongodb).
diff --git a/docs/Automation/trigger/cronjob.md b/docs/Automation/trigger/cronjob.md
index a62111432..80080901e 100644
--- a/docs/Automation/trigger/cronjob.md
+++ b/docs/Automation/trigger/cronjob.md
@@ -10,21 +10,19 @@ metadata:
next:
description: ''
---
-The schedule trigger is ideal for periodically running queries, fetching data, or even sending emails.
+The schedule trigger is ideal for periodically running queries, fetching data, or sending emails.
-For this guide, we will fetch the Stargazer count of a GitHub repository every working day at 9 a.m. and send a Discord notification.
+This guide fetches the Stargazer count of a GitHub repository every working day at 9 a.m. and sends a Discord notification.
## Creating the automation
-To create an automation with the schedule trigger, head over to the Automation Section and create a new Automation by clicking the `+` icon in the left-hand menu. A dialog will pop up, in which you can enter the name of your automation, and you can select the `Schedule` trigger.
+To create an automation with the schedule trigger, go to the Automation section and create a new automation by clicking the `+` icon in the left-hand menu. Enter a name and select the `Schedule` trigger.
-
-
-Then, click `Save`, and a new automation will be created using a `Schedule Trigger`.
+Then click `Save` to create the automation.
## Configuring the schedule
-The schedule trigger provides several ways to define when your automation should run. You can choose from a variety of presets or provide a custom cron expression.
+The schedule trigger provides several ways to define when your automation should run. You can choose from presets or provide a custom cron expression.
### Period
@@ -38,15 +36,15 @@ You can select the frequency of your schedule using the **Period** dropdown:
### Timezone
-By default, schedules use **UTC**. You can select a specific timezone (e.g., `Europe/London`, `America/New_York`) to ensure your automation runs at the correct local time, accounting for daylight savings shifts automatically.
+By default, schedules use **UTC**. You can select a specific timezone, such as `Europe/London` or `America/New_York`, to run at the correct local time.
### Next Executions
-The configuration panel displays a list of the next four scheduled execution times in your selected timezone. This helps you verify that your schedule is configured correctly.
+The configuration panel displays the next four scheduled execution times in your selected timezone.
## Advanced: Cron expressions
-If you select the **Cron expression** period, you can enter a series of numbers or stars. The order is from small to big:
+If you select the **Cron expression** period, you can enter a series of numbers or stars. The order is from smallest to largest:
* Minutes
* Hours
@@ -54,11 +52,11 @@ If you select the **Cron expression** period, you can enter a series of numbers
* Month
* Day of the Week
-We have presets available within the cron builder that can be selected to generate a cron expression for you. Additionally, for Premium or Enterprise tenants, you can use the AI assistant to generate expressions from a natural language prompt.
+The cron builder includes presets that can generate an expression for you. For Premium or Enterprise tenants, you can also use the AI assistant to generate expressions from a natural language prompt.
-Cron Scheduling can be very powerful, but you'll need to understand syntax. For every field, you can use the following expressions:
+Cron scheduling can be powerful, but you need to understand the syntax. For every field, you can use the following expressions:
-* **Plain numbers**: From 0 to 59 for minutes, 0-23 for hours, and 1-12 for Months.
+* **Plain numbers**: From 0 to 59 for minutes, 0-23 for hours, and 1-12 for months.
* **Ranges**: `5-10` in the minutes section would mean every minute from the 5th until the 10th minute.
* **Separations**: Multiple numbers or ranges, like `4,8,12` in the months column would be the 4th, 8th and 12th month only. Separations can also have ranges, such as `5-10,35-40` in the minutes column, which would be every minute between the 5th and 10th minute, and every minute between the 35th and 40th minute.
* **A `*`**: This means `every`. So if you have only stars everywhere, that means every minute, of every hour, of every day of every month.
@@ -68,25 +66,19 @@ Example: To run a job every working day at 3 a.m. UTC, you would use the followi
## Timestamp binding
A binding is available for the timestamp of when the schedule job was triggered.
-This returns the number of epoch milliseconds
-
-
+This returns the number of epoch milliseconds.
## Performing an API call
-Now that we've set up our `Schedule Trigger`, it is time to perform a REST call to fetch some information. For this tutorial, we've created a new [REST API](doc:rest) Data Source, added [Bindings](doc:rest-bindings) to the API call to allow customization, and we've set up a [Transformer](doc:transformers) to turn the data into quick, usable data. But you can set up whatever you want here, even regular queries to Data Sources.
+Now that you have set up the `Schedule Trigger`, perform a REST call to fetch some information. For this tutorial, create a new [REST API](doc:rest) data source, add [Bindings](doc:rest-bindings) to the API call, and set up a [Transformer](doc:transformers) to turn the data into usable output. You can also use regular queries to Data Sources.
-If you want to read more about setting up a REST API, Bindings, and Transformers, we recommend checking out the linked pages.
+If you want to read more about REST APIs, bindings, and transformers, see the linked pages.
We end up with something like this:
-
-
-When running a test you can see the API call and the transformer worked as expected, and it returns the stargazer count of the Github repository of choice.
+When you run a test, you can see the API call and transformer return the stargazer count for the chosen GitHub repository.
-
-
-For reference, we've transformed the result from the public Github Search API like this:
+For reference, the result from the public GitHub Search API is transformed like this:
```javascript
return data.items[0].stargazers_count
@@ -94,18 +86,14 @@ return data.items[0].stargazers_count
## Using the data
-Now we have a scheduled trigger and an API call is done at that exact time. The only thing that remains is using the result of the API call. For this purpose, we're going to be firing a Discord notification.
-
-First, we're retrieving a DIscord Webhook URL, setting the botname, optionally add a nice profile picture, and then set the binding to display the number of stars.
+Now that the scheduled trigger runs the API call, use the result to send a Discord notification.
-
+First, retrieve a Discord Webhook URL, set the bot name, optionally add a profile picture, and then set the binding to display the number of stars.
-As you can see, we formulated a nice message, and used the `value` from the API call from `step 1`. Our binding ends up to look like this:
+The message uses the `value` from the API call in `step 1`. The binding looks like this:
```handlebars
You now have {{ steps.External Data Connector.response }} stargazers
```
-And this works, because our Discord bot just sent this message
-
-
\ No newline at end of file
+That works because the Discord bot sends the message.
diff --git a/docs/Automation/trigger/email-listener.md b/docs/Automation/trigger/email-listener.md
index 97fcb5644..6b6781301 100644
--- a/docs/Automation/trigger/email-listener.md
+++ b/docs/Automation/trigger/email-listener.md
@@ -7,25 +7,29 @@ metadata:
description: kba_5hdtu
robots: index
---
-The Email Received automation triggers whenever a new email arrives in a mailbox that you've connected to. It connects to your IMAP server (Gmail, Outlook, Microsoft 365, or any IMAP-compatible provider), checks for new messages on a short polling interval, and fires when an email matches your conditions. When it runs, the trigger exposes the message’s subject, sender, recipients, date, plain-text, and HTML body so you can use them in later steps.
+The Email Received trigger runs when Budibase finds a new message in a connected mailbox. It polls an IMAP server, checks for unread messages, and exposes the email subject, sender, recipients, date, plain-text body, and HTML body for later steps.
-If your flow also needs to send or reply to emails, set up SMTP first. The IMAP trigger doesn’t require SMTP to receive messages, but you’ll need SMTP for outbound mail. For a quick setup guide, follow the steps on the Email (SMTP) setup page here.
+If you also need to send email, configure SMTP separately. Receiving messages only requires IMAP.
## Setup
-To configure the trigger, create a new **Email received** type automation. Click on the initial step and enter your IMAP host and port, enable SSL/TLS, and provide the mailbox username and password
+To configure the trigger:
-
+1. Create a new **Email received** automation
+2. Open the trigger step
+3. Enter the IMAP host and port
+4. Enable SSL/TLS if required
+5. Add the mailbox username and password
-
+Use the mailbox settings to point Budibase at the folder you want to monitor. In most cases you only need the host, port, security settings, username, and password.
-The Email received trigger listens to the mailbox you set up within the initial step of the automation. When a new email arrives, Budibase detects it, converts the message into a payload (subject, from/to, date, body text/HTML), and starts your automation. You can then map those fields in downstream steps (e.g., create a record with From/To/Body). Budibase tracks each email’s UID, so the same message won’t trigger twice.
+When a new email arrives, Budibase converts it into a payload and starts the automation. The same message will not trigger twice.
## Microsoft 365 / Azure Entra ID
-If your mailbox is hosted in Exchange Online, the Microsoft guide on enabling IMAP access is relevant for the mailbox-level settings, but Budibase also needs an OAuth2 connection and Exchange Online app/service-principal setup.
+If your mailbox is hosted in Exchange Online, you also need OAuth2 and mailbox-level IMAP access.
-Budibase's IMAP trigger uses the OAuth2 client credentials flow for machine-to-machine access. In practice, that means:
+Budibase's IMAP trigger uses the OAuth2 client credentials flow for machine-to-machine access. Configure it as follows:
1. Create an OAuth2 connection in Budibase under **Workspace Settings > Connections > APIs**.
2. Set the token endpoint to your Entra tenant, for example `https://login.microsoftonline.com//oauth2/v2.0/token`.
@@ -33,7 +37,7 @@ Budibase's IMAP trigger uses the OAuth2 client credentials flow for machine-to-m
4. Set the scope to `https://outlook.office365.com/.default`.
5. In the **Email received** trigger, choose **OAuth2** as the authentication method and select the Budibase OAuth2 connection.
-For the Exchange Online side, the mailbox must allow IMAP and the app must be granted access to the mailbox. A working setup looks like this:
+The mailbox must allow IMAP and the app must have access to it. A working setup looks like this:
```powershell
Connect-ExchangeOnline -UserPrincipalName redacted@bongotown.onmicrosoft.com -Device
@@ -71,22 +75,16 @@ If IMAP does not appear in the app registration's API permissions, add the Excha
}
```
-The Graph `User.Read` entry above is included from the working tenant setup that was shared with us. Budibase's IMAP trigger does not require Microsoft Graph for mail polling itself; the important part is the Exchange Online IMAP permission and mailbox access.
+The Graph `User.Read` entry above comes from the working tenant setup that was shared with us. Budibase does not need Microsoft Graph for polling itself. The important part is Exchange Online IMAP access plus mailbox permission.
-`your-graph-scope-id` should be replaced with the scope ID for the Graph permission you grant, and `your-exchange-online-role-id` should be replaced with the Exchange Online role ID from your tenant setup or the value your engineer provides.
+Replace `your-graph-scope-id` and `your-exchange-online-role-id` with the values from your tenant.
-The Microsoft article is mainly useful for the IMAP mailbox setting itself: Microsoft Learn.
+The Microsoft article is mainly useful for the mailbox IMAP setting itself: Microsoft Learn.
## Example
-This is an example of how you can use Budibase's email automation trigger to populate a table with the contents of an email. This is useful when testing this feature, as you can see what is being output from the automation
+Use the trigger outputs to map the message subject, sender, and body into a table.
-
+When an email is received, trigger a create row action and map `_body`, `_from`, and `_subject` into the target table. If the subject is empty, you can still create the row from the sender and body fields.
-When an email is received, it triggers the _create row_ action, which we have set up to populate our 'emailz' table based on three fields: _body_, _from_, and _subject_. These have been selected from the Email's trigger outputs
-
-
-
-Now, each time an email is received, Budibase runs the automation and creates a new row in 'Emailz' with the sender’s email address in _from_ and the message content in _body_. There may not always be a subject, but if there is, it will be in _subject_
-
-
\ No newline at end of file
+Each time an email is received, Budibase runs the automation and creates a new row with the sender, body, and subject values.
diff --git a/docs/Automation/trigger/index.md b/docs/Automation/trigger/index.md
index 1d2b2e663..b7bf35d88 100644
--- a/docs/Automation/trigger/index.md
+++ b/docs/Automation/trigger/index.md
@@ -1,6 +1,6 @@
---
title: Triggers
-excerpt: ''
+excerpt: Start automations from events
deprecated: false
hidden: false
metadata:
@@ -10,24 +10,44 @@ metadata:
next:
description: ''
---
-Each automation must have a single trigger step, which is essentially a watcher - waiting for a particular condition to be met, at which point the process will kick off.
+Every automation starts with a trigger.
-
+The trigger listens for an event and starts the automation when that event occurs.
-Currently, there are six ways to trigger an automation:
+## Trigger types
-1. When a new record/row is created in a table
-2. When a new record/row is deleted in a table
-3. When a new record/row is updated in a table
-4. An external trigger in the form of a JSON Webhook
-5. When a certain action happens in an app
-6. CRON trigger (trigger this automation at a specific time or day)
-7. Email received in a specific inbox
+Budibase supports these trigger types:
-> 📘 Row creation/update/deletion triggers
->
-> When utilising row-based triggers, it should be noted that this only works for rows created through Budibase. If you are using an external data source, like say Postgres or MySQL, then rows created externally within the database will not trigger automations.
+* Row created
+* Row deleted
+* Row updated
+* Webhook
+* App action
+* Cron schedule
+* Email received
-Triggers are internal to the Budibase system and therefore cannot easily be altered - if you have an idea for a good trigger, then [let us know](https://github.com/Budibase/budibase/discussions)!
+## Row triggers
-It is also possible to create custom triggers using the external Webhook - create an external application that will trigger on your required conditions, and target your Budibase Webhook!
+Row triggers respond to rows created, updated, or deleted through Budibase.
+
+Changes made directly in an external datasource do not fire Budibase row triggers.
+
+## Webhook triggers
+
+Use a webhook trigger when another application needs to start a Budibase automation.
+
+Use this when:
+
+* The event starts in another service
+* You need to pass a JSON payload into Budibase
+* You want an external system to call Budibase on demand
+
+## Trigger guidance
+
+Keep triggers narrow and predictable:
+
+* Use one trigger per automation
+* Pick the trigger type that matches the source of the event
+* Use app action, cron, or email triggers only when they fit the workflow
+
+If Budibase does not provide the trigger type you need, use a webhook-based integration.
diff --git a/docs/Automation/trigger/row-actions.md b/docs/Automation/trigger/row-actions.md
index 073ee89ac..099101070 100644
--- a/docs/Automation/trigger/row-actions.md
+++ b/docs/Automation/trigger/row-actions.md
@@ -12,55 +12,39 @@ metadata:
next:
description: ''
---
-A Row Action is configured against the table but can be enabled or disabled per view. This allows you to control which users can trigger specific workflow stages.
+Row actions let you trigger a workflow from a table row.
-Using row actions allows you to securely ensure that only specific users with the correct permissions can progress row data through a pipeline
+Configure the action on the table, then enable it per view to control who can run it.
## Setup
-Below are the steps on how you can setup your first row action.
-
1. Go to the data and select the table/view you wish to create a row action on
2. Click the **Row actions** button beside **Access** above your table
-
-
+ This opens the row action controls for the selected table or view.
3. A context menu will appear, providing you with some information about row actions
4. Click the **Create row action** button
-5. You will be prompted with a modal. Type a relevant name for the row action e.g. accept, deny, assign etc
-
-
+5. Type a name for the row action, such as `accept`, `deny`, or `assign`
6. Click the **Create** button
-7. You will now be redirected to the row action automation. You can begin building out your workflow for this row action here
-
-
-8. Returning to your table, you can see how many row actions have been created for a table. You can also create views with relevant access roles. This will carry over to your row actions. If a certain user doesn't have the correct permissions, they won't have access to the row action. These can be managed on a view-per-view basis by toggling them on and off.
-
-
-9. After toggling these on, you should see a new dropdown button on the table's right-hand side.
-
-
-10. Clicking on the dropdown button on the far right will display all available row actions. You can then click one of the row actions to trigger the corresponding workflow.
-
-
+7. Build the automation that should run for this row action
+8. Return to the table and enable the action for the views and roles that should see it
+9. Users will then see a dropdown on the table row with the available actions
## Design
-A new action with the same name has been added to trigger these row actions. You can learn more about configuring this action [here](doc:data-actions#row-action).
+Row actions are also available as a design-time action. See [Row action](doc:data-actions#row-action) for configuration details.
-From the design perspective, there are two ways to quickly add row actions to your components.
+There are two common ways to add them to a screen:
-The first is with a [table](doc:table) component. Start by adding a table component to your design area. Select the table component, scroll to the bottom of its settings, and click `Add button`. This will let you choose either a custom button or a row action. The button title will match the name of your row action.
+1. Add a [table](doc:table) component and use `Add button` in the settings panel.
+2. Choose a custom button or a row action.
+3. The button label matches the row action name.
-
+The button appears in the table and can be made collapsible if you have several actions.
-When selected, a new button will appear in the table. You can review the button's settings to see how it’s configured. There is also an option to make the button collapsible, which is helpful when you have numerous buttons attached to the table, as they will be grouped into a dropdown menu.
-
-Once everything is set up, users can use these buttons to trigger your row action automation, allowing users to process your workflows.
+You can use the button to trigger the row action automation directly from the table.
### Form block
-A [form block](doc:form-block) can also generate buttons with row actions. The setup here is even easier than the table, as it will auto-generate the button based on your selected schema. Ensure you use an update form type, as row actions are meant to progress existing data through a workflow.
-
-
+A [form block](doc:form-block) can also generate row action buttons automatically. Use an update form type so the action works on existing data rather than creating new rows.
-You can also add a [Row action](doc:data-actions#row-action) to any component that allows [actions](doc:actions) to be applied.
\ No newline at end of file
+You can add a [Row action](doc:data-actions#row-action) to any component that supports [actions](doc:actions).
diff --git a/docs/Automation/trigger/row-trigger-filters.md b/docs/Automation/trigger/row-trigger-filters.md
index bd8d30fb3..2e1e4f334 100644
--- a/docs/Automation/trigger/row-trigger-filters.md
+++ b/docs/Automation/trigger/row-trigger-filters.md
@@ -10,42 +10,18 @@ metadata:
next:
description: ''
---
-# Row Triggers with Filters
+Row trigger filters let you limit when a row created or row updated automation runs.
-## Overview
+## Use filters
-Row triggers allow you to automate actions based on changes in your database. With the addition of filters, you can now create more specific triggers that only fire when certain conditions are met.
+Add a filter when the automation should only run for rows that match a specific condition, such as an `Approved` flag or a particular status value.
-## Types of Row Triggers
+## Row created
-1. **Row Created**: Triggers when a new row is added to the database.
-2. **Row Updated**: Triggers when an existing row is modified.
+For row created triggers, the filter is checked against the new row. If the row matches, the automation runs.
-## Adding Filters to Triggers
+## Row updated
-Both "Row Created" and "Row Updated" triggers now support filters. Filters allow you to specify conditions that must be met for the trigger to fire.
+For row updated triggers, the automation only runs when the row changes into a matching state.
-### Example filter
-
-
-
-This filter would cause the trigger to fire only when the Approved column of the affected row equals "true".
-
-## Special Behavior for Row Updated Trigger
-
-The Row Updated trigger has a special behavior when filters are applied:
-
-By default, the trigger will only fire when a row transitions from not meeting the filter criteria to meeting the filter criteria.
-
-### Example
-
-Consider a filter: `Status = Approved`
-
-* The trigger will fire when a row's status changes from "Requested" to "Approved".
-* The trigger will not fire if a row's status is already "Approved" and some other field (like "Due Date") changes.
-
-This behavior ensures that the trigger only fires when the specific condition you're interested in is met, rather than on any update to a row that already meets the condition.
-
-## Row Created Trigger Behavior
-
-For the Row Created trigger, the filter is applied to the newly created row. If the new row meets the filter criteria, the trigger will fire.
\ No newline at end of file
+For example, if the filter is `Status = Approved`, the trigger runs when a row changes from `Requested` to `Approved`. It does not run again on later edits if the row is already approved.
diff --git a/docs/Automation/trigger/row-triggers.md b/docs/Automation/trigger/row-triggers.md
index eb8bfa1c7..52bafed68 100644
--- a/docs/Automation/trigger/row-triggers.md
+++ b/docs/Automation/trigger/row-triggers.md
@@ -12,28 +12,20 @@ metadata:
next:
description: ''
---
-## Row created
+Row triggers let you run an automation when a row is created, deleted, or updated.
-This trigger type allows users to create automations based on newly created rows. When a row is created, the entire row’s object will be passed to the automation and referenced through trigger Bindings. Various steps can be added to manipulate the data, such as updating information in another table or sending the data to a [REST API](doc:external-data-connector) endpoint.
+## Row created
-
+Use this trigger when a new record should start a workflow. The trigger passes the full row object into the automation, so later steps can create related records, send notifications, or sync the new data to another system.
## Row deleted
-This trigger works similarly to created rows. It will pass in a row’s object whenever a row is deleted. This can then be used to do other operations e.g. removing it from an API or deleting a related row elsewhere within Budibase.
-
-
+Use this trigger when the workflow should react to a delete. Common uses include cleaning up related records, revoking access, or logging the change elsewhere.
## Row updated
-Like the other two triggers listed above, this automation will run whenever a row within the chosen table is updated.
-
-
-
-It will provide both the old row's object (before the change). Additionally you also have access to the current values these are displayed as Bindings the names will reflect the columns of your table.
-
-
+Use this trigger when a workflow should react to edits on an existing row. The trigger exposes both the previous row values and the updated row values, so you can compare fields and branch the automation based on what changed.
-With the addition of the oldRow object, you can now perform more complex operations, such as testing if a specific value has changed and then continuing or ending the automation based on that condition.
+## Typical outputs
-This trigger can also be used similarly to the creation and deletion triggers, such as updating additional related records or posting updates to an API or query.
+The row payload is available as trigger bindings. For updates, the automation also receives the old row object, which is useful when you only want to continue if a specific field changed.
diff --git a/docs/Automation/trigger/webhook.md b/docs/Automation/trigger/webhook.md
index 66b0237bd..686bfd695 100644
--- a/docs/Automation/trigger/webhook.md
+++ b/docs/Automation/trigger/webhook.md
@@ -12,84 +12,48 @@ metadata:
next:
description: ''
---
-> A webhook is an HTTP-based callback function that allows lightweight, event-driven communication between two APIs.
+Use a webhook trigger when an external service needs to start a Budibase automation.
-Contextually, webhooks allow Budibase to listen for an external application event, and trigger an automation. A payload will be included in the trigger that gives Budibase information about the event.
+The external system sends a JSON payload to Budibase, and Budibase uses that payload to start the automation.
-
+## Set up the trigger
-{`
-
-`}
+1. Create a new automation.
+2. Select `Webhook` as the trigger.
+3. Open the trigger settings.
+4. Copy the schema URL.
+5. Send a sample payload to that URL.
+6. Refresh the builder after Budibase accepts the schema.
-## Setup
+Use a payload from the external service you plan to connect, then shape the automation around the fields Budibase receives.
-Create a new automation and select `Webhook` as the trigger.
+## Link the external app
-This will present you with a box. To view the details for this step, click on it this should open a side panel with additional information.
+Copy the trigger URL from Budibase into the external system’s webhook configuration.
-
+Make sure the external service sends JSON to the trigger URL.
-The **Schema URL** is required to tell Budibase what payload to expect from the external application.
+## Build from the payload
-Click the `Set Up Webhook` button. This will bring up a dialog with the schema URL. Copy the schema URL.
+Once Budibase knows the payload shape, you can add steps that use the trigger data.
-Let's assume for this example we want to save a row to a Budibase table any time a new issue is created in our GitHub repo.
+Common patterns include:
-Conveniently we can grab an [example payload](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#webhook-payload-example-when-someone-edits-an-issue) from the GitHub webhook docs.
+* Creating a row from the payload
+* Filtering on a field such as an action or event type
+* Triggering a follow-up automation
-Next open Postman, or any other REST query tool, and POST the example payload to the schema URL.
+## Test the workflow
-
+After publishing the automation:
-After you receive the `200 OK` response, you should now see that Budibase has successfully picked up on the schema. Click _Finished_.
+1. Send a real event from the external app.
+2. Confirm the automation runs.
+3. Check the automation history for the result.
+4. Verify any downstream row or action changes.
-
+## Related guides
-Finally click the `Publish` button in the top right of the screen to make the automation live.
-
-## Link the trigger application
-
-Copy the **Trigger URL** from your Budibase automation and paste it into the _Payload URL_ of your GitHub webhook.
-
-Make sure **Content type** is _application/json_
-
-
-
-Click `Add webhook`.
-
-To test, add an new issue in your GitHub repo. Within a minute, you will be able to see that the automation was triggered via the [Automtion history](https://docs.budibase.com/docs/logs).
-
-
-
-## Trigger body
-
-Now that we know that GitHub events are being picked up by our Budibase application, lets do something with the event data. For example, we may want to create a new row in our own 'Issues' table.
-
-Create a new 'Issues' table with a text _Name_ column, a number _Number_ column, and an options type _Tag_ column.
-
-
-
-
-
-Next within the _Automation_ tab, add an additional [Condition](https://docs.budibase.com/docs/conditions-1) step. This is because we only want to create a row if the GitHub action type is _opened_.
-
-Add the `{{ trigger.action }}` binding to the _Reference Value_ field.
-
-
-
-Select condition _Equals_ comparison value 'opened'.
-
-
-
-Add another automation step for **Create Row**. Select _Use bindings_ as well as the 'Issues' table. Fill in the bindings.
-
-
-
-You can click `Finish and test automation` to verify your automation. Don't forget to press **Publish** when you're done.
-
-Now when a new issue is created in your GitHub repo, you should see a new row in your Budibase table.
-
-
-
-
+* [Automation steps](doc:automation-steps)
+* [Automation testing](doc:activating-and-testing)
+* [Automation logs](doc:logs)
diff --git a/docs/Bindings/bindings.md b/docs/Bindings/bindings.md
index 0870a74ba..5b2131a85 100644
--- a/docs/Bindings/bindings.md
+++ b/docs/Bindings/bindings.md
@@ -37,12 +37,6 @@ For more information, the full catalogue of helpers is available
-`}
-
## Tips and tricks
It is important to remember that Handlebars does allow quite complex statements, which can make use of comparative logic (especially with our comparison helpers). Below we've detailed a few tips which should help you get started with bindings and Handlebars syntax.
diff --git a/docs/Bindings/introduction-to-bindings.md b/docs/Bindings/introduction-to-bindings.md
index 5cb7c6856..765fea320 100644
--- a/docs/Bindings/introduction-to-bindings.md
+++ b/docs/Bindings/introduction-to-bindings.md
@@ -10,186 +10,103 @@ metadata:
next:
description: ''
---
-Bindings are a core concept in Budibase, and expand the functionality of your Workspaces beyond the standard [autogenerated screens](https://docs.budibase.com/docs/table-block#autogenerated-screen).
+Bindings connect dynamic values to fields in Budibase. Use them to pass data, format values, and perform calculations.
-You can think of bindings as solving these high-level problems:
+## When to use bindings
-* passing data from one location to another
-* formatting data for display
-* performing calculations
+Use bindings when you need to:
-***
+* Display data from a table, form, or user context
+* Pass a value from one component to another
+* Use a query parameter or screen state in a data source
+* Format output before it is shown to the user
+* Run JavaScript for more complex logic
-## Bindings drawer
+## Open the bindings drawer
-In its simplest form, a binding is a reference to a dynamic value, and is expressed with a syntax known as [Handlebars](doc:bindings) by default.
+Look for the lightning icon next to a field that supports bindings.
-Whether within the **Design** or **Automate** sections, look out for the lightning bolt icon next to input fields. This indicates that the field supports binding values.
+1. Click the lightning icon.
+2. Choose a binding category.
+3. Search for the value you want.
+4. Insert the binding into the field.
-
- The 'Text' property of the Heading supports bindings
-
+You can also type a binding directly into a text field using double braces.
-You can directly type bindings into the text field using double braces, e.g. `{{ Field Value }}`.
-
-However, this won't give you access to the binding drawer, which provides auto-completion or the option to switch to JavaScript syntax.
-
-To open the bindings drawer, click on the lightning icon. The drawer will slide up from the bottom of the screen.
-
-You will see a list of binding categories on the right-hand side, which you can search and dig into to find the binding expression you would like to add. You can read more about binding categories and their scope [here](doc:bindings-scope)
-
-It is worth noting that you can combine static text with binding values, which are distinguished by blue-highlighted text.
-
-
-
-From the demonstration above, you may have noticed that each binding indicates the data type along the right-hand side, which will usually be a *String* (text) value such as the first name of the logged-in user. You may also have noticed in the above GIF that it is valuable whenever a binding is hovered. This is very useful as it lets users see what's being output before saving their changes. This only works if there is awareness of the value of the binding.
-
-If an arrow is present, this means that the item is a category that must be drilled into. Regarding the binding `{{ Current User.firstName }}`, we are accessing the *firstName* text property from the *Current User* object.
-
-### Auto completion
-
-While the bindings panel provides an easy way to explore available binding options, there is an even faster way when you get comfortable with using bindings.
-
-Within the text area type `{{`. This will immediately prompt a dialog with a scrollable list of binding options. Start typing to filter this list; you filter by the property name or category name. Next, use the arrow keys to navigate the list, and press the *Enter* key to insert the binding you want.
-
-
-
-### JavaScript
-
-As you can see from the tabs in the bindings drawer, there is the option to use JavaScript instead of text. The benefit of [Using JavaScript](doc:javascript) is that it provides the maximum flexibility for manipulating data, although it will obviously require a little bit of experience in using the language.
-
- If you have any existing text, clicking the JavaScript tab will provide a warning prompt - click `Yes - discard text` to proceed if you wish, but you can always click the `Cancel` button to revert your changes.
-
-
- Switching to JavaScript will overwrite the existing text value
-
-
-Auto-completion works in a similar way for JavaScript, however the difference is the symbol used to denote the binding. Instead of using curly braces, type the `$` dollar symbol to access the bindings menu.
-
-
-
-> 📘 Return value
->
-> JavaScript bindings are considered to be functions, and thus you must always use the `return` keyword to return a value.
-
-***
-
-
-
-## Passing data around
-
-Bindings open a link between your [Data](doc:data) and your [Screens](doc:screens) and [Automations](doc:automation-steps), but they can also be used to connect two screens, or link components within a screen.
-
-For example you may have a [REST query](doc:rest-queries) or [Custom SQL query](https://docs.budibase.com/docs/data-sources#custom-queries). What if you wanted the app user to input a query param, or provide a filter value? You will need bindings to do this, with configuration needed in both the **Data** section and the **Design** section.
-
-### Data config
-
-Whenever you add a query for any [Datasource](doc:data-sources) you have the option of adding bindings to be used in your query.
-
-For example consider this [SQL query](https://docs.budibase.com/docs/sql-datasource#add-query):
-
-
- Get the row entry for Georgi
-
-
-To make this query generically useable for retrieving an entry for any name, click the `Add binding` button. Type in a unique name for the binding, and provide a default value. The default value allows you to test that your query is working correctly, and in the case of a *Read* query, configure the [Schema](https://docs.budibase.com/docs/data-sources#schema).
-
-Next you need to reference this binding within your query. You do this through the curly braces syntax:
-
-
-
-### Design config
-
-With the bindings configured in the Data section, we can now pass values from the Design section into our query.
-
-As this is a *Read* query, we can use it as the source for a [Data provider](doc:data-provider). Click the cog icon to open the bindings drawer.
-
-
- Selecting a Read query as a data source
-
-
-
- Query bindings drawer
-
+```text
+{{ Field Value }}
+```
-You can now see a preview of our query, and also an input field for the *firstName* binding is available. A common scenario would be to use a form text field binding for implementing searching, which would look similar to:
+This is useful for simple values, but the drawer gives you autocomplete and access to JavaScript bindings.
-
- Passing the value from a 'first\_name' form field into a SQL query
-
+## Use JavaScript bindings
-### Further reading
+Switch to the JavaScript tab when you need logic that text bindings cannot provide.
-* [Bindings in forms](doc:form-bindings)
-* [Using query bindings](https://docs.budibase.com/docs/data-sources#using-query-bindings)
-* [REST bindings (tutorial)](doc:rest-bindings)
-* [URL variables](doc:url-parameters)
+JavaScript bindings must return a value.
-***
+```javascript
+return $("Current User.firstName");
+```
-
+Use JavaScript bindings when you need to:
-In addition to passing data from one location to another, you can perform calculations on values, or choose the format you wish to display data.
+* Transform values before display
+* Combine multiple fields
+* Build dynamic URLs
+* Calculate totals or conditional output
-## Tutorial: Calculating the total cost of selected items
+## Pass data between queries and screens
-To start, create a new workspace with the [Sample data](https://docs.budibase.com/docs/data-sources#sample-data). In this example we will be focused on the **Inventory** table.
+Bindings are commonly used in both the Data and Design sections.
-In the Design section, add a [Form](doc:forms), nest a [Data provider](doc:data-provider) for the **Inventory** table, and nest a [Container](doc:container).
+### In Data
-The idea is to have a dropdown for selecting the inventory items on the left, and a table that shows the details for the selected items on the right. At the bottom of the table there will be a headline that prints the sum total *Purchase Price* of the selected items.
+Add a binding to a query when the input value should come from the app.
-Add a [Multi-select picker](doc:multi-select-picker) to the container. Type in a unique field name, and in the style tab provide a fixed width of `200px`.
+Example use cases:
-Under the *Options source* setting, select *Data provider* and then select the data provider we had added earlier. Set the *Label column* to 'Item Name', and the *Value column* to 'Item ID'.
+* Filter a read query by a form field
+* Pass a screen parameter into a REST query
+* Reuse the same query for multiple records by changing the binding value
-
- Component tree so far
-
+### In Design
-
- Multi-select settings
-
+Use a data provider, form field, or component state to supply the binding value.
-With our multi-select configured, now we want to add a table to show more info for the selected items, and add our sum total of prices.
+Typical flow:
-Add a container below the multi-select, and nest another Data provider, which will also be for the **Inventory** table.
+1. Add the binding in the query.
+2. Use the query in a data provider or component.
+3. Map a screen field, state value, or user value into the binding.
+4. Test the query with a known default value before publishing.
-> 🚧 Duplicate data providers
->
-> 🚧 Performance Tip: To optimise performance, minimise the number of data providers on your screen, especially for the same data source or table query.
->
-> It's best practice to avoid duplicating data sources unnecessarily. Ideally, use a single data provider to query the table's data and then rely on additional providers to filter that data. This reduces server requests and enhances performance.
->
-> In this case, a duplicate data provider is required because one is filtered for the table, while the picker needs access to all rows.
->
-> For a performance boost, consider using [use custom options](https://docs.budibase.com/docs/option-picker#using-custom-options) if the list of options is static.
+## Calculate values
-Click `Define filters` and add the following:
+You can use bindings to calculate display values as well as data values.
-
+Example uses:
-Make sure to `Save`! This will filter the data provider based on the selected items from the multi-select field.
+* Sum selected prices
+* Format a status label
+* Build a download URL
+* Show or hide content based on a condition
-Nest a [Table](doc:table) component under the data provider. Add `min-height: 320px;` to the [Custom CSS](doc:custom-css) of the table - this is so the total text at the bottom of the table doesn't move as items are selected. Alternatively, you can drag the elements around the screen to get the same layout if you use the new grid layout.
+## Good practice
-After selecting the table on the right of the screen, you can show and hide table columns by toggling on and off the columns.
+Keep bindings:
-Finally, below the table component, add a [Headline](https://docs.budibase.com/docs/displaying-text#headline) component, and right-align the text. Add the following binding after switching to the JS tab:
+* Clear
+* Short
+* Predictable
+* Easy to test
-```javascript
-const autoID = $("New Form.Fields.items");
-const arrayOfItems = $("Inventory Dataprovider.Rows");
-const totalPurchasePrice = arrayOfItems.reduce((acc, item) => {
- if (autoID.includes(item["Item ID"])) {
- return acc + item["Purchase Price"];
- }
- return acc;
-}, 0);
-
-return `Total £${totalPurchasePrice}`;
-```
+Prefer direct values and simple expressions. Use JavaScript only when the binding needs real logic.
-#### Result
+## Related guides
-
+* [Bindings](doc:bindings)
+* [Bindings scope](doc:bindings-scope)
+* [JavaScript bindings](doc:javascript)
+* [Form bindings](doc:form-bindings)
+* [URL parameters](doc:url-parameters)
diff --git a/docs/Bindings/snippets/time-ago-snippet-function.md b/docs/Bindings/snippets/time-ago-snippet-function.md
index 8387bff05..f4a411c8e 100644
--- a/docs/Bindings/snippets/time-ago-snippet-function.md
+++ b/docs/Bindings/snippets/time-ago-snippet-function.md
@@ -10,20 +10,20 @@ metadata:
next:
description: ''
---
-This quick guide demonstrates how to create a JavaScript snippet that converts date times into hours, days, minutes, and seconds from the current moment.
+This quick guide shows how to create a JavaScript snippet that converts date times into hours, days, minutes, and seconds from the current moment.
## Steps
-1. Create a Budibase internal table this should include some form of date (In my case I will theme this around a blog)
-2. Add data to your table
+1. Create a Budibase internal table that includes a date field. In this example, it is based on a blog.
+2. Add data to your table.
3. Go to the design screen and add a [Card block](doc:card)
-4. Fill in the relevant bindings for Title, description etc
+4. Fill in the relevant bindings for Title, description, and other fields.
5. Go to the subtitle bindable area.
-6. Switch the tab from text to javaScript
-7. Click on the `>` icon and create a new snippet called `timeago`
-8. Paste the below code into it and click save
+6. Switch the tab from text to JavaScript.
+7. Click the `>` icon and create a new snippet called `timeago`.
+8. Paste the code below into it and click save.
```Text JavaScript
return function(pastTimeString) {
@@ -60,11 +60,11 @@ return function(pastTimeString) {
}
```
-9. Click the snippet and add the date binding you wish to use inside its brackets
-10. Then click save
+9. Click the snippet and add the date binding you want to use inside its brackets.
+10. Click save.
-## The end result!
+## The end result
diff --git a/docs/Bindings/url-parameters.md b/docs/Bindings/url-parameters.md
index 056194be6..f038356d3 100644
--- a/docs/Bindings/url-parameters.md
+++ b/docs/Bindings/url-parameters.md
@@ -10,54 +10,45 @@ metadata:
next:
description: ''
---
-Sometimes you need to pass information between screens. This takes two main forms:
+Sometimes you need to pass information between screens. There are two common ways to do that:
1. [Link element](doc:link)
2. 'Navigate To' Action
-A common example is opening a detailed view for a row entry.
+One common example is opening a detailed view for a row.
## Navigate To
-Some templates such as the *Client Contact List* app use row buttons instead of links. Tables also allow you to define actions [on row click](https://docs.budibase.com/docs/table-block#on-row-click).
+Some templates, such as the *Client Contact List* app, use row buttons instead of links. Tables also let you define actions [on row click](https://docs.budibase.com/docs/table-block#on-row-click).
In these cases, navigation is controlled by the navigation action:
-
+Here, pass the client's row ID into the URL path.
-Here we are passing the row id of the client into the URL path.
+In the `/clients/:id` details screen, you can use that row ID. A word prefixed with a colon in a route denotes a path variable.
-In the `/clients/:id` details screen we will be able to make use of this row id.\
-A word prefixed with a colon within a route denotes a path variable.
+Access the ID using the `{{ URL.id }}` Binding.
-The id is accessed using the `{{ URL.id }}` Binding
+For example, if you only want to show the chosen client, add the following Filter to the client's Data Provider.
-For example, because we only want to show the chosen client, we can add the following Filter to the clients' Data Provider
-
-
-
-This can then be used in conjunction with a [Repeater](doc:repeater) to access the fields of the filtered client:
+Use it with a [Repeater](doc:repeater) to access the fields of the filtered client.
## Multiple variables
-Whilst passing a row id is the most common use case, you can pass many variables into the URL, with each being separated by a slash, e.g. `/clients/:id/:companyId/:timestamp/:customName/:etc`
+While passing a row ID is the most common use case, you can pass multiple variables in the URL, each separated by a slash, for example `/clients/:id/:companyId/:timestamp/:customName/:etc`.
-A detailed example can be found in this [discussion](https://github.com/Budibase/budibase/discussions/5299#discussioncomment-2530261).
+A detailed example is available in this [discussion](https://github.com/Budibase/budibase/discussions/5299#discussioncomment-2530261).
## Testing URL Variables
-A `Temporary URL variables` input has been added to the Screen Settings area. This feature displays a preview of how a URL will be structured, such as `edit/{variable}`, allowing users to pass variables dynamically while building screens.
-
-
-
-This feature lets you see real-time data population within components without previewing or publishing changes. This helps you quickly understand how different records affect the layout of your screens.
+A `Temporary URL variables` input is available in the Screen Settings area. It previews how a URL is structured, such as `edit/{variable}`, so you can pass variables while building screens.
-
+This lets you see data populate components without previewing or publishing changes. It helps you understand how different records affect the layout.
### Benefits:
-* Eliminates the need to hit "Preview repeatedly" or "Publish" to test variable driven screens.
+* Eliminates the need to repeatedly use Preview or Publish to test variable-driven screens.
* Speeds up development by reducing context switching.
-* Provides instant feedback on how records populate within components.
\ No newline at end of file
+* Provides instant feedback on how records populate within components.
diff --git a/docs/Data/_order.yaml b/docs/Data/_order.yaml
index 7b38e2456..09cc5ae86 100644
--- a/docs/Data/_order.yaml
+++ b/docs/Data/_order.yaml
@@ -4,6 +4,7 @@
- datasources-copy
- rest
- sql-datasource
+- sql-workflow
- transformers
- environment-variables
- pdf-exporting
diff --git a/docs/Data/budibasedb/attachments.md b/docs/Data/budibasedb/attachments.md
index 3d4789eec..0dbc939d0 100644
--- a/docs/Data/budibasedb/attachments.md
+++ b/docs/Data/budibasedb/attachments.md
@@ -1,119 +1,49 @@
---
title: Attachments
-excerpt: Learn more about how Budibase manages static files.
+excerpt: Store uploaded files in Budibase DB
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5hf5u
+ description: ''
robots: index
next:
description: ''
---
-Almost every web application needs to manage static files in some way.
-
-These could be images, text files, audio files and more. With budibase, you can upload your static files to your budibase applications with ease. We are going to cover how to upload and display your files both inside the budibase builder and in your web applications themselves.
-
-### New Field Types for Attachments
-
-We've recently introduced new field types for attachments, replacing the previous unified attachment field. Now, attachments are categorised into two distinct types: **Attachment** and **Attachment List**. Let's explore the differences:
-
-## Field Types
-
-In Budibase, static files are managed through the Attachment field type. Both attachment and attachment list fields support various static file formats, including images, CSV files, and more.
-
-### Attachment
-
-This field type allows the upload of a single attachment at a time. You cannot add additional attachments to this field. When looking more into the JSON data related to this field you will see that this is stored as an object were previously it was an array.
-
-### Attachment List
-
-Here, you can include multiple attachments of different types, such as CSV files, images, Word documents, and more.
-
-To create an attachment or attachment list field type, simply add a new column to a new or existing table and select either the Attachment, or Attachment list type. While an attachment field can accommodate as many attachments as needed, please note that the maximum file upload size per file is 20MB.
-
-## Uploading attachments
-
-When you create a new row in your table you will be presented with a [Dropzone component](https://docs.budibase.com/docs/attachment) for your attachment field that you can use to upload files.
-
-> 📘
->
-> A **Dropzone** component is a commonly used file upload component in web applications. It allows you to drop files from your computer into the dropzone area, or to navigate your file system to find the file that you want.
-
-Your attachments will be displayed like any regular field - inside grids and tables, forms and more Budibase components.
-
-To see attachments in action, take a look at the following example section. We will build a small app for storing country data using Budibase attachments.
-
-Attachments are stored in [MinIO](https://docs.budibase.com/docs/accessing-minio).
-
-## Example
-
-In this example, we will be creating a simple web application that will be used to store information about different countries around the world.
-
-### Creating the Countries table and columns
-
-Let's begin by creating a table to store data for our countries.
-
-Next, we need to create columns in our `Countries` table. Let's look at the information we need to include about each country and decide which data types are the best to use for our columns.
-
-* **Country Name**: Country names are just letters, so a [Text](doc:text) type is a good fit here.
-* **Country Population**: We should use a [Number](doc:number) type for this.
-* **Country Flag**: This will require an image file - here is where **Attachment** types come in.
-* **Travel Guide**: We can store PDF travel guides for each country here. Let's use another **Attachment** type.
-
-Watch the video below to find out how to set up your table as detailed above.
-
-{`
-
-`}
-
-## Creating rows and uploading data
-
-Now that we have set up our columns, we can start to add some rows to our `Countries` table. You will now notice that on row creation, you will be presented with dropzone components inside the row creation popover for both of our attachment types - `Flag` and `Travel Guides`.
-
-Let's create a row for Spain. Follow along with the video below to find out how.
-
-Here are the files used in the video below if you don't have your own:
-
-* [Spanish Flag Image](https://upload.wikimedia.org/wikipedia/en/thumb/9/9a/Flag_of_Spain.svg/750px-Flag_of_Spain.svg.png)
-* [Spanish Travel Guide](https://www.madeforspainandportugal.com/wp-content/uploads/2016/01/Spain-with-Made-for-Spain.pdf)
-
-{`
-
-`}
-
-Muy bien! 🇪🇸
-
-### Front-end
-
-Navigate over to the *Design* tab in the builder. Add a screen and select `List view`. This will create an [Autogenerated screen](https://docs.budibase.com/docs/table-block#autogenerated-screen). Now we can preview our application and interact with our attachments!
+Use an attachment column when a row needs to store uploaded files.
-Click the preview button on the top right of the builder. You will be navigated to your app in your default browser. Log in with the credentials that you set up when you initially created your app and you will see the homepage of your new app. Navigate to the automatically created "Countries" link in your navbar - you should see your countries listed in a table:
+Budibase supports:
-
+* Single attachments
+* Attachment lists
-***
+## Create an attachment column
-## Manage access
+1. Open the table
+2. Click `Create column`
+3. Choose `Attachment` or `Attachment List`
+4. Save the column
-By default, the table **write** access will be *Basic*. This means if you have a public form, you may see an error message: `Session not authenticated` when trying to upload files.
+## Supported files
-To fix this, click on the `Manage access` button.
+Attachment columns can store common file types, including:
-
+* Images
+* PDFs
+* CSV files
+* Word documents
+* Audio files
-Set the write access to **public**
+The maximum file size is 20 MB per file.
-
+## Use attachments in forms
-**Note** this will allow anyone to upload a file to your app instance, so use with caution!
+When a form targets the table that owns the attachment column, Budibase shows an upload control for that field.
-You must also ensure that the table with your target attachment field is selected as the [form schema](https://docs.budibase.com/docs/forms#form-schema). Budibase needs to know the target field of the attachment upload.
+Use the matching table as the form schema so Budibase knows where to store the uploaded file.
-> 👍
->
-> 
+## Notes
-> ❗
->
-> 
\ No newline at end of file
+* Attachments are stored in MinIO
+* Public uploads require the table write access to allow the upload
+* If the form schema does not match the target table, the upload cannot be saved
diff --git a/docs/Data/budibasedb/barcodeqr.md b/docs/Data/budibasedb/barcodeqr.md
index 4b03d5880..dde84e331 100644
--- a/docs/Data/budibasedb/barcodeqr.md
+++ b/docs/Data/budibasedb/barcodeqr.md
@@ -1,40 +1,38 @@
---
title: Barcode/QR
-excerpt: This topic describes the barcode/qr data types, supported in Budibase
+excerpt: Store barcode or QR values in Budibase DB
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5ifb6
+ description: ''
robots: index
next:
description: ''
---
-Barcodes and QR codes come in a huge range of formats, from simple product numbers to complex text patterns and URLs.
-
-To accommodate the wide variety of content the field can handle, the Barcode/QR column type is stored as text, or a string.
-
-Fields configured as Barcode/QR types in the database will appear as [Barcode/QR components](https://docs.budibase.com/docs/barcodeqr-field) in the builder.
+Use a Barcode/QR column when a field should store text that is displayed as a barcode or QR code.
-## Creating a Barcode/QR column
+## Create a Barcode/QR column
-The process for creating the column only takes a few seconds.
+1. Open the table
+2. Click `Create column`
+3. Set the type to `Barcode/QR`
+4. Save the column
-1. First, navigate to your Datasource and click the `+` button along the top-right
-2. Give the column a Name, e.g "ProductCode"
-3. Select **Barcode/QR** from the **Type**
-4. Click **Save Column**
+## Use Barcode/QR columns
-
+Barcode/QR columns are stored as text, so they can hold values such as:
-## Convertible types
+* Product codes
+* URLs
+* Reference strings
-In Budibase, depending on the field type, you can update the type of a column to another
+In the builder, these columns appear as Barcode/QR components.
-In this instance, a Barcode/QR can be updated to any of the following and vice versa.
+## Convert the column
-* [Text](doc:text)
-* [Options](https://docs.budibase.com/docs/text#options)
-* [Long Form Text](https://docs.budibase.com/docs/text#long-form-text)
+You can convert a Barcode/QR column to or from related text types such as:
-This can be handy when you've been manually entering QR code and Barcode data for years as `Text` and you finally switch the field type to Barcode/QR!
\ No newline at end of file
+* Text
+* Options
+* Long Form Text
diff --git a/docs/Data/budibasedb/boolean-truefalse.md b/docs/Data/budibasedb/boolean-truefalse.md
index cba9c682d..c74bb5090 100644
--- a/docs/Data/budibasedb/boolean-truefalse.md
+++ b/docs/Data/budibasedb/boolean-truefalse.md
@@ -1,6 +1,6 @@
---
title: Boolean (True/False)
-excerpt: ''
+excerpt: Store yes/no values in Budibase DB
deprecated: false
hidden: false
metadata:
@@ -10,15 +10,22 @@ metadata:
next:
description: ''
---
-True/False is a data type that has one of two possible values (usually denoted as true/false, yes/no, Boolean). Within your UI, this is usually displayed in the form of a checkbox.
+Use a boolean column for values that only have two states, such as yes/no or true/false.
-## Creating a Boolean column
+## Create a boolean column
-1. First, click the `+` button along the top-right
-2. Give your column a name
-3. Choose 'Boolean' as your data type
-4. Click `Save Column`
+1. Open the table
+2. Click `Create column`
+3. Enter a column name
+4. Set the type to `Boolean`
+5. Save the column
-## Using the boolean data type
+## Use boolean columns
-In the Design section, you can pull in information from your true/false column, using the [Repeater](doc:repeater) component and [bindings](doc:introduction-to-bindings)
+Boolean columns are commonly shown as:
+
+* Checkboxes
+* Toggles
+* Conditional display rules
+
+Use them when a value should only be on or off.
diff --git a/docs/Data/budibasedb/datetime.md b/docs/Data/budibasedb/datetime.md
index 7afee62e7..47f14f920 100644
--- a/docs/Data/budibasedb/datetime.md
+++ b/docs/Data/budibasedb/datetime.md
@@ -1,91 +1,50 @@
---
title: Date/Time
-excerpt: ''
+excerpt: Store date and time values in Budibase DB
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5ifeq
+ description: ''
robots: index
next:
description: ''
---
-The date/time data type is important when using components or displaying information that relies on dates and times.
-
-The [Date picker](https://docs.budibase.com/docs/date-picker) component, for example, can utilize the date field. Dates within Budibase comply with ISO 8601 formatting, e.g. `2023-04-24T13:12:04.622Z`. Thankfully, the date picker makes data entry easier.
-
-## Creating a Date/Time column
-
-1. First, click the `+` button along the top-right
-2. Give your column a name
-3. Choose date/time as your data type
-4. Enter some optional constraints
-5. Click `Save Column`
-
-
-
-## Constraints
-
-You can provide an **earliest** and **latest** date within your date/time column. This allows you to enforce a constraint such that dates added must fall *before the latest* and *after the earliest* dates specified.
-
-As with all other fields you can make it **required**.
-
-These constraints can easily be edited after creation by hovering over the date field and clicking the three dots icon.
-
-### Ignore time zones
-
-When this option is checked, the values selected in date pickers will be saved exactly as they are represented into the target database. Regardless of the timezone the browser is in, you will always see the same exact timestamp when this option is selected.
-
-As an example, here is a snippet of a table where the browsers locale was changed to 3 different timezones and the time 12:00 was picked in each. You can see that the column "BB no timezone" (which has *Ignore time zones* enabled) will always show the same time, whereas the "BB normal" column (where the flag is toggled off) reflects the browser timezone changes.
-
-
-
-It is possible, but not recommended, to change the *Ignore time zones* flag on tables that already have dates with timezones. Changing this flag does not change existing data, but it will change how your existing data is presented in the browser.
-
-By default this setting is toggled off.
-
-> 📘
->
-> This setting is also available for PostgreSQL and MySQL tables.
-
-## Working with dates
-
-As mentioned above, dates in Budibase are given as standard ISO 8601 date/time strings. You can make use of bindings to transform that date string into whatever you need to display.
-
-### Tutorial: Calculating the duration of an event
-
-Say we have a table *Events* with two date columns: *Start Time* and *End Time*. To calculate the duration of each event, we can create a Formula column and then use Bindings to calculate the difference between the two dates.
-
-It's not possible to perform mathematical operations on a date string, so the first step is to convert the date into milliseconds. The value is a count of how many milliseconds the date is before or after [the UNIX epoch in 1970](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date). We can do this using the **date helper** where bindings are available. The date helper takes the date value, the desired output format and an optional timezone as arguments.
-
-To format *Start Time* in milliseconds using the date helper, pass the Start Time value and 'x' as the date format. You can see the other available output formats in the [Day.js format documentation,](https://day.js.org/docs/en/display/format) which is what the date helper uses behind the scenes. Our Handlebars expression is `{{ date Start Time 'x' }}` and the output is **1685100000000**.
+Use a date/time column when a field needs to store timestamps or dates.
-The next step is to do the same for *End Time* and then using the **subtract helper**, subtract the ms value for *Start Time* from it. This will return the length of the event in milliseconds. Here's how it looks with the helpers chained up:
+Budibase stores date/time values in ISO 8601 format.
-`{{ subtract (date End Time 'x' ) ( date Start Time 'x' ) }}`
+## Create a date/time column
-Now we have the duration of the event in milliseconds, the final step is to convert this value into something more useful. We can use the **duration helper** which takes our time value and the unit to calculate a human readable duration. By default this helper uses the [Day.js Humanize Duration function.](https://day.js.org/docs/en/durations/humanize)
+1. Open the table
+2. Click `Create column`
+3. Set the type to `Date/time`
+4. Add any constraints you need
+5. Save the column
-Here's the final binding and the result:
+## Common constraints
-`{{ duration (subtract (date End Time 'x' ) ( date Start Time 'x' )) "milliseconds" }}`
+Use constraints to limit the values users can save:
-
+* Required
+* Earliest date
+* Latest date
-Of course, you can also use JavaScript bindings for more complex time calculations. All of the helpers that make use of Day.js are also available there.
+## Ignore time zones
-### Tutorial: Calculating the time until an event
+Use `Ignore time zones` when you want Budibase to save the selected value exactly as entered.
-The same principle can be used for calculating the time since a past date or the time until a future date. In this case you would only use one date value and compare it to the current time instead of another date. To get the current time in milliseconds, use `{{ date now 'x' }}`
+Leave it off when you want the browser timezone to affect the stored timestamp.
-In all cases, subtract the earliest date from the latest date. For working out how long ago past events were, I used this binding:
+## Use date/time values
-`{{ duration (subtract (date now 'x' )( date Start Time 'x' )) "milliseconds" }} ago`
+Date/time columns work well with:
-
+* Date picker components
+* Bindings that format dates
+* Formula columns that calculate durations or date differences
-### Further reading
+## Notes
-* [Bindings Tutorial: Calculate age from Date of Birth field](https://docs.budibase.com/docs/form-bindings#tutorial-calculate-age-from-dob-field)
-* [Handlebars Bindings: The date helper and chaining helpers ](https://docs.budibase.com/docs/bindings)
-* [JavaScript Bindings: Using a date column](https://docs.budibase.com/docs/javascript#using-a-date-column)
\ No newline at end of file
+* The setting is also available for PostgreSQL and MySQL tables
+* Changing `Ignore time zones` does not rewrite existing data
diff --git a/docs/Data/budibasedb/index.md b/docs/Data/budibasedb/index.md
index b2f2a2640..d9d56d99a 100644
--- a/docs/Data/budibasedb/index.md
+++ b/docs/Data/budibasedb/index.md
@@ -1,6 +1,6 @@
---
title: Budibase DB
-excerpt: ''
+excerpt: Use Budibase DB for native app data
deprecated: false
hidden: false
metadata:
@@ -10,153 +10,89 @@ metadata:
next:
description: ''
---
-Budibase DB is our native datasource. Any data you add here is stored directly on your Budibase instance and can be used by any apps or automations within your Workspace.
+Budibase DB is the native datasource for your workspace.
-## Creating a table
+Use it when you want data stored directly in Budibase and shared across apps and automations in the same workspace.
-You can create a table when you are in the **Data** section of the workspace.
+## Create a table
-Click `Add source` and then click the `Create new table` button.
+1. Open the **Data** section
+2. Click `Add source`
+3. Choose `Create new table`
+4. Enter a table name
+5. Save the table
-
- Create a new table using this button
-
+## Add columns
-
- Creating a new Budibase DB table
-
+Add columns from the table grid.
-### Adding columns
+1. Open the table
+2. Click the `+` button
+3. Choose the column type
+4. Set the column name and options
+5. Save the column
-To add new columns to an existing Budibase DB table, navigate to the table you want to edit, then click the `+` button along the top-right of the grid view. This will display a popover where you can configure the column you want to add.
+## Choose a display column
-
- Adding a long form 'Address' field
-
+The display column is the value shown in relationship fields and other linked views.
-### Editing columns
+Set a display column when you want records to appear by name instead of ID.
-1. Step into the table through the sidebar
-2. Hover over the column name you want to edit
-3. Click the three dots and select *Edit column*
+## Use auto columns
-
- Click the pencil icon to edit column
-
+Budibase DB supports system-managed auto columns such as:
-### Selecting the display column
+* `Auto ID`
+* `Updated By`
+* `Updated At`
-The display column is an important aspect when you're setting up relationships in tables. The column you select as a display column will be the one visually displayed inside a relationship column in another table.
-
-Selecting a column during the table creation is only possible if you import data using a `CSV` file. To select a display column in any other situation, you can set it from the edit column dropdown.
-
-
- Use as display column
-
-
-### Auto-columns
-
-Budibase DB supports system-managed auto columns that populate themselves when rows are created or updated.
-
-When adding a column, select *Auto column*, then choose the subtype offered in the picker.
-
-
- Selecting the 'Auto Column' data type
-
-
-The current builder exposes `Auto ID`, `Updated By`, and `Updated At` in the add-column picker. `Created By` and `Created At` are still supported by the data model and may appear in existing tables or generated schemas, but they are not currently shown as add-column options in the UI.
-
-| Name | Description | UI availability |
-| :---------- | :------------------------------------------------------- | :------------------------------------------- |
-| Auto ID | An automatically generated numeric ID | Available in the picker |
-| Updated By | The user who updated the row last | Available in the picker |
-| Updated At | The time when the row was updated last | Available in the picker |
-
-> 👍 Create table from CSV
->
-> You can [Import a CSV/JSON](doc:csv-import) file as a new Budibase table.
-
-***
+Use these when you need values generated by Budibase instead of entered by a user.
## Data types
-Within Budibase DB there are several data types to choose.
-
-| Name | Description |
-| :-------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- |
-| [Text](doc:text) | Storage of (relatively) short text |
-| Long Form Text | Allows you to store large amounts of texts, also supports markdown |
-| Options | Predefined list of options of which one can be selected |
-| [Multi-select](doc:multi-select) | Predefined list of options of which multiple can be selected |
-| [Number](doc:number) | Storage of number |
-| [Boolean (True/False)](doc:boolean-truefalse) | Storage of true/false |
-| [Date/time](doc:datetime) | Storage of a date with a time |
-| [Attachments](doc:attachments) | Storage of a file. Limited to 20MB per file |
-| [Relationships](doc:relationships) | Creates a link between this table and another table |
-| [Formula](doc:formula) | Allows you to set a formulated column, which will be calculated based on what you define. You can use handlebars or JavaScript |
-| [JSON](doc:json) | Allows you to store JSON within a row, with the option to define a schema |
-| [Barcode/QR](doc:barcodeqr) | A text representation of a barcode or QR code |
-| Auto Column | Auto columns are system-managed columns. Add them after table creation by selecting `Auto column`. |
-
-If using an external [SQL Datasource](doc:sql-datasource), the native SQL data types will be mapped to one of these Budibase types for use in your workspace. For more detail, look [here](https://github.com/Budibase/budibase/blob/178fd100af09ac1bc8b8395db52c629c572dc4bb/packages/server/src/integrations/utils.ts) for how the mappings are handled.
-
-## Column definitions
-
-Common column options:
-
-| Property Name | Description |
-| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
-| Name | The name of the column, used for querying and displaying |
-| Type | The type of data you want to store. |
-| Use as table display column | Enabling this will make the newly created column the display column. This will also automatically make the field required |
-| Primary index | Enabling this allows you to search in this field |
-| Secondary index | When primary index is in use, you can enable a second index for searching |
-
-
-
-| Property Name | Description | Data Types |
-| :------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
-| Enable Rich Text Format Support | Enabling this will allow you to store markdown | Long Form Text |
-| Options | The select-able options available for this column, one per line. | Options, Multi-select |
-| Table | Allow a relationship to the specified table | Relationship |
-| Column name in other table | The relationship created will also be visible in the related table, this will set the new column name on the related table | Relationship |
-| Formula | Allows you to set a formulated column, which will be calculated based on what you define. Allows you to define handlebars and JavaScript | Formula |
+Common Budibase DB column types include:
+
+* Text
+* Long Form Text
+* Options
+* Multi-select
+* Number
+* Boolean
+* Date/time
+* Attachments
+* Relationships
+* Formula
+* JSON
+* Barcode/QR
## Constraints
-The following column constraints are available:
-
-| Constraint Name | Description | Data Types |
-| :-------------- | :----------------------------------------------------------------------------------- | :--------- |
-| Required | When enabled, a value must be set for this field | All |
-| Maximum Length | The maximum length that can be stored in this column | Text |
-| Min Value | When set, any value entered in the database lower than this number will be rejected | Number |
-| Max Value | When set, any value entered in the database higher than this number will be rejected | Number |
-| Earliest | When set, any date before the specified date is rejected for new rows | Date/Time |
-| Latest | When set, any date after the specified date is rejected for new rows | Date/Time |
+Use constraints to validate values in Budibase DB tables.
-> 📘 External datasource constraints
->
-> You can add Budibase constraints to external table columns, however this is entirely for form validation and will not restrict the data that is saved.\
-> Instead, you must add data constraints yourself to any external table schemas.
+Common constraints include:
-***
+* Required
+* Maximum length
+* Minimum value
+* Maximum value
+* Earliest date
+* Latest date
-## Data sync between environments
+Constraints on external tables are used for form validation only.
-When using the Budibase DB, there are two environments to consider.
+## Development and production data
-The **Development** environment represents the data that you see and interact with when using the app builder, which includes the *Data* and *Design* sections, and the [App preview](https://docs.budibase.com/docs/quickstart#preview-and-publish).
+Budibase DB keeps separate development and production data.
-If in doubt, you can see if you are in the dev environment from the URL, which will show `/app/app_dev_abc...` in the path. In production, the URL will appear without the `_dev` part.
+* Development data is what you see in the builder
+* Production data is what your app users see
-**Production** represents your live environment. This is the data that your End Users will interact with. When in the app builder, production data will continuously sync and update your development data, although a page refresh may be needed to see the new changes.
+When you publish an app, development changes are pushed to production.
-Whenever you [Publish](https://docs.budibase.com/docs/quickstart#preview-and-publish) your app, any edits, inserts or deletes made in your dev data will be pushed into production, which may overwrite existing data.
+## Related pages
-> 📘 External datasources
->
-> Things are a little different when using an external database such as [SQL](doc:sql-datasource).\
-> In this case the data in either environment will read and write to the database that you connected to.
->
-> You can use [Environment variables 🔒](doc:environment-variables) to make use of development and production databases.
+* [CSV Import](doc:csv-import)
+* [Text](doc:text)
+* [Number](doc:number)
+* [Boolean (True/False)](doc:boolean-truefalse)
+* [Options](doc:options)
diff --git a/docs/Data/budibasedb/json.md b/docs/Data/budibasedb/json.md
index 243c1a6a2..3477aaf97 100644
--- a/docs/Data/budibasedb/json.md
+++ b/docs/Data/budibasedb/json.md
@@ -1,223 +1,50 @@
---
title: JSON
-excerpt: ''
+excerpt: Store structured JSON data in Budibase DB
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5hf9e
+ description: ''
robots: index
next:
description: ''
---
-The JSON data type is available for internal tables. JSON fields have a schema, which can be configured when creating or editing the column. Having the correct schema is important as the builder uses this to know what bindings are available and what form fields are available. Extra data that is not in the schema can be stored, but no bindings will exist for it.
-
-The JSON field schema can be edited with either a form or by providing an example JSON payload. If you are using deep JSON structures (e.g. multiple levels of objects or arrays) then you will need to use the JSON schema editor, as the form editor only goes one level deep.
-
-Schema editor button when creating or editing a JSON column:
-
-.png")
-
-## Editing JSON (Form)
-
-Using the JSON form editor, you can update your JSON without having to write JSON.
-
-.png")
-
-## Editing JSON (raw)
-
-Using the JSON schema editor, you can write and edit raw JSON.
-
-.png")
-
-## Displaying data within a JSON column
-
-JSON field schema is merged with table schema in client apps, and basically treated as new top level columns. This means you can filter on nested values, or display them inside tables, as if they were normal fields in your table. Here's an example of a table showing all columns inside the table containing the JSON field above:
-
-.png")
-
-## Displaying JSON arrays
-
-In that screenshot, you can see that it has pulled out fields from within the JSON field and displayed them automatically. It also is showing the JSON fields as a whole (as it shows all columns by default) - but you can easily control what gets displayed by using the table setting:
+Use a JSON column when a row needs to store structured data with nested fields or arrays.
-.png")
+JSON columns include a schema so Budibase can expose the available bindings and form fields.
-***
+## Create a JSON column
-## Filtering
+1. Open the table
+2. Click `Create column`
+3. Set the type to `JSON`
+4. Define the schema
+5. Save the column
-You can filter using fields inside your JSON. Here's an example of filtering using the nested value Car.Make:
+Use the form editor for simple structures. Use the raw JSON schema editor when you need nested objects or arrays.
-.png")
+## Use JSON columns
-***
+Budibase treats schema fields as normal top-level fields in tables, forms, filters, and bindings.
-## Data bindings
+That means you can:
-Data bindings will be automatically generated for all available fields inside your JSON, including nested fields (e.g. *Car.Make*). Data bindings will be generated until an array is hit, at which point no further bindings can be generated. You can read about how arrays are handled down below. Bindings are also provided for the JSON field as a whole and any array fields, both of which integrate very nicely with JS bindings.
-
-Here's an example of all the data bindings provided for the JSON field above. My table is called **People** and my JSON column is called **Data**.
-
-.png")
-
-***
+* Bind to nested values
+* Filter on nested values
+* Display nested fields in tables
+* Save nested fields from forms
## Arrays
-Arrays inside JSON fields can be used as data sources. For this example, I've added a new property called **Friends** inside my JSON field, which is an array.
-
-```json
-"Friends": [
- {
- "Name": "Bill",
- "Address": {
- "Number": 1,
- "Street": "High Street",
- "City": "New York"
- }
- },
- {
- "Name": "Ben",
- "Address": {
- "Number": 18,
- "Street": "Shore Road",
- "City": "Carrickfergus"
- }
- },
- {
- "Name": "Bert",
- "Address": {
- "Number": 249,
- "Street": "Mountain Cresent",
- "City": "Lima"
- }
- }
- ]
-```
-
-Here's an example using a table block to display data of *Friends*. The structure is:
-
-* a [Data Provider](doc:data-provider) with [Repeater](doc:repeater) (to get the rows that contain the JSON fields),
-* then a table block (to show the data inside the array inside the JSON field).
-
-
-
-You can see from this screenshot that the schema correctly determines all available fields inside the array objects and is listing them in the table. If you have an array of primitives (e.g. an array of numbers, or strings) then a fake schema entry **value** is generated. You can then display data inside primitive arrays using *Field.something.value*.
-
-You can nest arrays as deep as you like. If your JSON structure looked something like this:
-
-```json
-{
- "countries": [{
- "name": "England",
- "cities": [{
- "name": "London",
- "boroughs": ["Barnet", "Brent", "Camden", "Croydon", "Harrow", "Sutton"]
- }]
- }]
-}
-```
-
-Then you can use a repeater on *Column.countries*, then put a repeater block inside that and target *countries.cities*, then put another repeater block inside that and target *cities.boroughs*, to finally get out the borough values. You can chain this as long as you like and the schema will always be available.
-
-***
-
-## JSON form field
-
-There is a new form field component for JSON fields. It will render a text area that pretty prints the content as JSON. It also has forced validation to ensure that the input is valid JSON.
-
-The new JSON field component:
-
-.png")
-
-Validation enforcing JSON syntax:
-
-.png")
-
-***
-
-## JSON form integration
-
-Since nested JSON fields are considered normal fields, you can bind a form component to a nested JSON field. In my example, I have the field *Car.Make*. If I want to update this, I can simply add a text field and bind it to *Car.Make*, then save my whole row as normal. This will transparently update the value inside the JSON field with no extra configuration.
-
-Binding a form component to a nested JSON field:
-
-.png")
-
-When generating form components automatically (via the `Update form fields` button on [Field groups](doc:field-groups)) the builder will always insert an actual JSON field form component for the whole JSON field, rather than individual inputs for every nested property. You can always add/remove fields as you see fit if you want to be able to directly update some nested fields.
-
-If you have a form targeting a subsection of your JSON field, then the best way to save your full row value is to write a small [JavaScript Binding](doc:javascript) and make use of the new *Form.Value* binding (which is an object of the whole value of the form) to insert the value into your JSON field. This is only really needed when you are iterating over JSON arrays and want to update one of the array elements.
-
-### Example: Questionnaires
-
-1. Add a **Topic** table with a *Name* column and a JSON type *Questionnaires* column with the following schema:
-
-
-
-2. After clicking `Save Column`, insert a row for each of your topics.
-
-
-
-3. Create a [Screen](doc:screens) for the `Topic` table. Insert a [Cards block](https://docs.budibase.com/docs/blocks#cards-block). Assign the *Title* property to the topic name binding (`{{ New Cards block.Topic.Name }}`, and optionally set the *Subtitle* to display the number of questionnaires: `Questionnaires: {{ length New Cards block.Topic.Questionnaires.values }}`
-4. Tick the `Link card title` setting, and provide the following URL: `/new-questionnaire/:topicId`
-
-
-
-5. Create another screen, and set the route to the link destination:
-
-
-
-6. Add a [Repeater block](https://docs.budibase.com/docs/blocks#repeater-block) with a filter on the *topicId*
-
-
-
-7. Add a [Headline](https://docs.budibase.com/docs/displaying-text#headline) for the topic name, with the following binding: `Topic: {{ Topics Repeater block.Topic.Name }}`.
-8. Nest a [Form](doc:forms) within the repeater block. Select the *Questionnaires* column as the schema.
-
-
-
-9. Now you'll be able to add the form fields as defined by the JSON schema earlier.
-
-
-
-10. Finally add a submit [Button](doc:button). `Define actions` and select the **Save Row** action. Choose the repeater block as the *Datasource*, with *Topic* as the target table. Click `Add Column` so that the JSON *Questionnaires* array can be populated correctly.
-
-
-
-```javascript Questionnaires.values
-return [...($("Topics Repeater block.Topic.Questionnaires.values") || []), {
- title: $("Questionnaire Form.Fields.title"),
- rating: $("Questionnaire Form.Fields.rating"),
- comments: $("Questionnaire Form.Fields.comments")
-}];
-```
-
-#### Result
-
-
-
-
-
- Q1
-
+JSON arrays can be used with repeaters and table blocks.
-
- Q2
-
-
-
+When an array contains objects, Budibase exposes the object fields automatically.
-
-
-
-
-
+When an array contains primitives, Budibase exposes a `value` field.
-
-
-
-
-
-
+## Notes
-
\ No newline at end of file
+* The schema controls which nested fields are available in the builder
+* Fields not in the schema can still be stored, but they will not have bindings
+* Use the JSON form component to validate input before saving
diff --git a/docs/Data/budibasedb/multi-select.md b/docs/Data/budibasedb/multi-select.md
index 6a5e9f27d..9a9393623 100644
--- a/docs/Data/budibasedb/multi-select.md
+++ b/docs/Data/budibasedb/multi-select.md
@@ -1,157 +1,44 @@
---
title: Multi-select
-excerpt: Using the multiple options data type in Budibase
+excerpt: Store multiple selected values in Budibase DB
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5hfb6
+ description: ''
robots: index
next:
description: ''
---
-In Budibase, there are two data types that allow a value to be set from a list of options.
-
-The first is the [Options](https://docs.budibase.com/docs/text#options) type, which is compatible with the *Text* data type, and only allows a single option to be chosen.
-
-The **Multi-select** type is similar to the options type, but it allows you to select multiple options, and is not convertible to any other type. You can still select a single option or none at all.
-
-A common use of the multi-select data type is to pair it with the [Multi-select picker](doc:multi-select-picker) in your [Forms](doc:forms).
+Use a multi-select column when a field should allow more than one value from a fixed list.
-
- Multi-select column
-
+## Create a multi-select column
-
- Selecting multiple options
-
+1. Open the table
+2. Click `Create column`
+3. Set the type to `Multi-select`
+4. Add the allowed options
+5. Save the column
-When a multi-select value is saved to a data table, the values will be presented in individual coloured pills:
+## Use multi-select columns
-
- Multi-select row presenting two selected options
-
+Multi-select columns work well with the [Multi-select picker](doc:multi-select-picker) in forms.
-
+Selected values appear as individual pills in tables and forms.
## Filtering
-A number of array based [filters](doc:searchfilter-data) are available when using a multi-select.
-
-### Contains
-
-> The multi-select value must contain all of the selected comparison options.
-
-
-
-
-
- Filter
-
-
-
- Result
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Does not contain
-
-> The multi-select value must not contain all of the selected comparison options.
-
-#### Single value
-
-
-
-
-
- Filter
-
-
-
- Result
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#### Multiple values
-
-Note that in this case only rows that have both *Senior* AND *Manager* are filtered out.
-
-
-
-
-
- Filter
-
-
-
- Result
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+Use array-based filters when you need to match against multi-select values:
-
+* `Contains`
+* `Does not contain`
+* `Has any`
-### Has any
+Use `Contains` when all selected comparison values must be present.
-> The multi-select value contains any of the selected comparison options.
+Use `Has any` when only one matching value is required.
-
- Has any: Senior or Manager
-
+## Notes
-
- Result
-
\ No newline at end of file
+* Multi-select columns are not convertible to other types
+* They can still hold a single value or no value at all
diff --git a/docs/Data/budibasedb/number.md b/docs/Data/budibasedb/number.md
index a5584fb92..621d7e439 100644
--- a/docs/Data/budibasedb/number.md
+++ b/docs/Data/budibasedb/number.md
@@ -1,6 +1,6 @@
---
title: Number
-excerpt: This topic describes the number data types, supported in Budibase
+excerpt: Store numeric values in Budibase DB
deprecated: false
hidden: false
metadata:
@@ -10,23 +10,27 @@ metadata:
next:
description: ''
---
-A number column is a good choice if you want to store data like age, scores, IDs, etc. The number column type is important when you need to do calculations within your data. Within the Number data type, you can provide min and max values. This allows you to add additional validation to your data type.
+Use a number column for values such as quantities, scores, prices, and IDs.
-## Creating a number column
+## Create a number column
-The process for creating a number column takes a few seconds.
+1. Open the table
+2. Click `Create column`
+3. Enter a column name
+4. Set the type to `Number`
+5. Add any minimum or maximum values you need
+6. Save the column
-1. First, click the `+` button along the top-right
-2. Give your column a name
-3. Choose Number as your data type
-4. Give it a min of 1, and a max of 5
-5. Click Save Column
+## Use number columns
-## Using the number data type
+Number columns are useful when you need:
-In the Design section, you can pull in information from your number column, using the [Repeater](doc:repeater) component and [bindings](doc:introduction-to-bindings)
+* Calculations
+* Range validation
+* Numeric filtering and sorting
-## Important considerations
+## Notes
-* A number column can only include numeric values and cannot include letters and symbols.
-* Telephone numbers need to be stored as a text/string data type because they often begin with a 0 and if they were stored as an integer then the leading zero would be discounted.
+* Number columns only accept numeric values
+* Use text for values that can include leading zeros, such as phone numbers
+* Add min and max constraints when the valid range is limited
diff --git a/docs/Data/budibasedb/options.md b/docs/Data/budibasedb/options.md
index 6af343ec8..cfffeff56 100644
--- a/docs/Data/budibasedb/options.md
+++ b/docs/Data/budibasedb/options.md
@@ -1,8 +1,6 @@
---
title: Options
-excerpt: >-
- `Options `data type is a straightforward way to provide _options_ for a
- dropdown list or radio buttons in forms.
+excerpt: Store a fixed list of values in Budibase DB
deprecated: false
hidden: false
metadata:
@@ -12,71 +10,27 @@ metadata:
next:
description: ''
---
-
+Use an options column when a field should only allow a fixed set of values.
-### Adding an `Options` column
+## Create an options column
-To add an options column to your table, select "Options" from this dropdown list.
+1. Open the table
+2. Click `Create column`
+3. Enter a column name
+4. Set the type to `Options`
+5. Add the allowed values
+6. Save the column
-
+## Use options columns
-While creating the column, you will be able to add different options the user can select. These options can be edited later by clicking "Edit Column".
+Options columns work well for:
-
+* Dropdowns
+* Radio buttons
+* Fixed-status fields
-
+## Reorder options
-### Use in Forms
+Edit the column if you need to change the available values or their order.
-Adding a `Form Block` component to your page, and setting the `Data` to your table will automatically populate fields into your form. Opening the settings panel will allow you to customise settings around your input.
-
-
-
-
-
Setting
-
-
-
-
-
-
Label
-
Text that will display above the input
-
-
-
Placeholder Text
-
Text that is displayed the field before the user makes a selection. Defaults to "Choose an option"
-
-
-
On change
-
[Configureable actions](https://docs.budibase.com/docs/form-actions) that run on change.
-
-
-
Type
-
Chose between "Select" for a dropdown menu, or "Radio Buttons".
Radio buttons not quite what you're looking for? [Try Multi-select picker.](https://docs.budibase.com/docs/multi-select-picker)
-
-
-
Direction
-
(Only available for Radio Buttons type input) Displays the options in vertically or horizontally arranged list.
-
-
-
Default Value
-
Assigns a default value should the user fail to make a selection
-
-
-
Help Text
-
Text that displays underneath, preceded by a question mark icon
-
-
-
Alphabetical
-
When unchecked, options will appear in the order that they shown in your data column.
-
-
-
-
-#### Changing the order of your options
-
-
-
-To Change the order the options display in your form, firstly make sure "Alphabetical" is unchecked. Then, in the Data tab, click edit your column, and drag-and-drop your options. Alphabetical being checked will not change the order the options appear in the data tab, only the order in which they are displayed to the user in the form.
-
-
\ No newline at end of file
+Keep the list short and stable so the column stays easy to use in forms and filters.
diff --git a/docs/Data/budibasedb/relationships.md b/docs/Data/budibasedb/relationships.md
index 7730b745c..7fed9ebe2 100644
--- a/docs/Data/budibasedb/relationships.md
+++ b/docs/Data/budibasedb/relationships.md
@@ -1,6 +1,6 @@
---
title: Relationships
-excerpt: ''
+excerpt: Link rows between Budibase DB tables
deprecated: false
hidden: false
metadata:
@@ -14,64 +14,32 @@ next:
slug: custom-datasource
title: Datasource
---
-This section covers one of the more complex topics of your Budibase web app, the ability to create relationships between different rows. The relationship is bi-directional, in that when it is created, each row will have a relation to the other one. A row is also allowed to relate to many rows, meaning that many to many relationships can be created in your web app.
+Use relationships to link rows between tables.
-## Creating a relationship column
+Relationships are bidirectional, so each table can reference the other.
-1. Select two tables that the relationship should be formed between.
-2. Make sure that in both tables there is a column defined that can be used for display purposes. When creating this column make sure to set it with the *Display column* toggle.\
- 
-3. Click the *Create new column* button in either of the two tables and select the *Relationship* data type from the dropdown.\
- 
-4. Here you will need to select two options, the tables to which this column relates, and the name of the relationship column in the other table as well.\
- 
-5. At this point, you will now have a relationship formed between the two tables. If you create a row in one of the tables, then go to the other table and create a row you should see the first row that was created as an option in the relationship multi-select.\
- 
+## Create a relationship
-
+1. Make sure both tables have a display column
+2. Open one of the tables
+3. Click `Create new column`
+4. Choose `Relationship`
+5. Select the related table
+6. Set the column name in the other table
+7. Save the column
-## Example
-
-In this example, we will be creating a web app for tracking students, teachers, parking spots for teachers and classes in a school - specifically how they relate to each other.
-
-Specifically, we are going to break these down into the three relationship types that can be modelled with Budibase. Below we will map out the relationships that we are going to create, first describing them then walking through how to build them.
+## Relationship types
### One-to-many
-
-
-The second relationship we can model in Budibase is one of the most common, linking a single row to many other rows. Here we can see that a class can only be taught by a single teacher however a teacher is capable of teaching many different classes.
+Use one-to-many when one row can relate to many rows in another table.
### Many-to-many
-
-
-The last relationship we can model is arguably one of the most complexes but again very common in real-life systems. Here we can see that students are able to take many classes and each class can have many different students in it.
-
-### Building it in Budibase
-
-To create this in Budibase all we need to do is:
-
-1. Create the four tables, students, classes, teachers, and parking spots. In each of these, we will initially create a display text column and mark these as the *Display column -* for example, N*ame* or *Subject*.
-2. First, we will create the teacher to parking spot relationship, starting in the teacher table we create a relationship type column, call it `ParkingSpot` and set the table to parking spots - then name the column in the other table `Teacher`.
-3. Second, we create the teacher to classes relationship, hopefully, at this point, it should be clear how this is done. Simply create a relationship from the teacher table to the class table, name the column `Classes` and in the other table call the column `Teacher`.
-4. Lastly, we create the students to classes relationship, the same as before but set the relationship table to classes, call the column in the students table `Classes` and call the column in the other table `Students`.
-
-As you can see by creating three relationship columns we were able to make a fairly complex web of relationships, which can now be easily managed when creating/updating rows in any of the tables.
-
-### Video Demonstration
-
-Based on the example above here is a video showing how easy it is to create in the Budibase Builder!
-
-{`
-
-`}
-
-## Considerations
+Use many-to-many when both sides can relate to many rows and you have a join table.
-The Budibase DB in a NoSQL database. As such, there are implicit limitations when it comes to handling relational data for larger more complex applications.\
-Watch the video below for more information on deciding which database type is best to use.
+## Notes
-{`
-
-`}
+* Relationship columns are stored in Budibase DB, not in the display column itself
+* Use nullable foreign keys in join tables for many-to-many relationships
+* Budibase DB is best for simple relationship models
diff --git a/docs/Data/budibasedb/text.md b/docs/Data/budibasedb/text.md
index 7be4a4b2f..fb983f428 100644
--- a/docs/Data/budibasedb/text.md
+++ b/docs/Data/budibasedb/text.md
@@ -1,6 +1,6 @@
---
title: Text
-excerpt: This topic describes the string/text data types, supported in Budibase
+excerpt: Store short strings in Budibase DB
deprecated: false
hidden: false
metadata:
@@ -9,71 +9,33 @@ metadata:
robots: index
next:
description: ''
- pages:
- - type: basic
- slug: bindings
- title: Bindings
- - type: basic
- slug: repeater
- title: Repeater
---
-The text data type stores a string of UTF-8 characters. A text column is a good choice if you want to store data like names or addresses It is a common data type and one you are more than likely going to use.
+Use a text column for short strings such as names, addresses, labels, and identifiers.
-***
+## Create a text column
-## Creating a text column
+1. Open the table
+2. Click `Create column`
+3. Enter a column name
+4. Keep the type as `Text`
+5. Save the column
-The process for creating a text column takes a few seconds.
+## Use text columns
-1. First, click the `Create column` button.
-2. Give your text column a name - such as 'Name'.
-3. By default the text data type will be selected; so there's no need to change it.
-4. Click `Save Column`.
+Text columns work well with:
-***
+* Display text components
+* Text inputs
+* Formula columns that build or transform strings
-## Using the text data type
+## Convert text columns
-In the Design section, will be able to use a variety of components for [Displaying text](doc:displaying-text), as well as a number of form [Text inputs](doc:text-inputs) for accepting user input.
+You can convert a text column into a related type when the data needs a stricter shape.
-***
+Common conversions include:
-## Convertible types
+* Options
+* Long Form Text
+* Barcode/QR
-The text data type can be converted into another related type.
-
-
- Changing the Text type
-
-
-The most common reason to change the type of a text column will be to make it compatible with one of the following [Form](doc:forms) components:
-
-* [Options picker](doc:option-picker)
-* [Long Form Field](https://docs.budibase.com/docs/text-inputs#long-form-field)
-* [Barcode/QR Field](doc:barcodeqr-field)
-
-
-
-### Options
-
-
- Options type
-
-
-Allows a single value to be chosen from a dropdown of options.
-
-> 📘
->
-> Any existing text value will be kept, however it will appear as a *null* in the dropdown.
-
-
- Selecting an option
-
-
-
-
-### Long Form Text
-
-
- Long form text type
-
+Keep existing values in mind before changing the type, especially if the new type needs a different format.
diff --git a/docs/Data/budibasedb/users-1.md b/docs/Data/budibasedb/users-1.md
index fe0427c11..c6bddf20e 100644
--- a/docs/Data/budibasedb/users-1.md
+++ b/docs/Data/budibasedb/users-1.md
@@ -1,6 +1,6 @@
---
title: User
-excerpt: ''
+excerpt: Store Budibase users in Budibase DB
deprecated: false
hidden: false
metadata:
@@ -10,77 +10,46 @@ metadata:
next:
description: ''
---
-The Single User and Multi User column types allow you to select a Budibase user from a dropdown list as determined by the [Application access](doc:application-access). This list will include all admins of the tenant, and any members of the app.
+Use user columns when a row needs to reference one or more Budibase users.
-
- Note the slight variation in icons between Single User (team\_leader) and Multi User (team\_members) columns
-
+## Create a user column
-With the user column added, you can now click the `+` within your data grid view to select a user as identified by their searchable email address. When using a Single User column, if you try to add another user, the current user will be removed.
+1. Open the table
+2. Click `Create column`
+3. Choose `Single User` or `Multi User`
+4. Save the column
-> 👍 SQL support
->
-> The user column is available for both the Budibase DB and external [SQL databases](doc:sql-datasource).
->
-> In the case of SQL, when a user column is added within Budibase, it will create a varchar column in your table to hold the user id:
->
->
+The dropdown includes app members and tenant admins.
-
+## Use user columns
-***
+User columns are commonly used for:
-
+* Ownership
+* Assignment
+* Approvals
-## Usage
+In forms, use the [User Field](https://docs.budibase.com/docs/pickers#user-field) component.
-A common usage of user columns in your tables is to indicate *ownership* or *assignment*.
+## Filter by current user
-For example, I can add an 'Assignee' user column to a 'Jobs' table to indicate who is responsible for completing that job.
+Use `{{ Current User._id }}` to filter rows for the logged-in user.
-
+Use a contains filter when the column allows multiple users.
-When making use of a user column in your [Forms](doc:forms), you need to use the [User Field](https://docs.budibase.com/docs/pickers#user-field) component.
+## SQL support
-
+User columns also work with external SQL datasources.
-### Show current user records
+Budibase stores the user ID in a varchar column in the SQL table.
-For single user columns, you can [filter](doc:searchfilter-data) on the currently logged in user by using the `{{ Current User._id }}` [binding](https://docs.budibase.com/docs/users-table#current-user-bindings).
+## Migrate existing relationships
-
+Apps created before v2.11.0 can migrate user-table relationship fields to user columns.
-If your user column has *Allow multiple users* enabled, then the filter will be a [contains](https://docs.budibase.com/docs/multi-select#filtering) like so:
+1. Open the table
+2. Open the table menu
+3. Choose `Migrate to user column`
+4. Follow the prompts to create the new column
-
-
-
-
-***
-
-
-
-## Migrate to user column
-
-> 📘
->
-> As of **v2.11.0** you will no longer be able to create a relationship between the [Users table](doc:users-table) and a Budibase DB.
->
-> To accommodate this change, apps that were created before **v2.11.0** can migrate existing user table relationship fields to the new user column, as outlined below.
-
-1. Open your existing app and navigate to the Budibase table that contains a user relationship
-2. Click the hamburger menu, and select the last option: `Migrate to user column`
-
-
-
-3. Click `Continue` to create the new user column. As warned, you may need to update some existing bindings
-
-
-
-
- User column added
-
-
-This will remove the link from the **App users** table and can improve the performance of your apps.
-
-It should also be noted that if you were [displaying user data](https://docs.budibase.com/docs/fetching-data#display-user-specific-data) through a migrated relationship link, this would also need to be updated as shown in the 'Usage' section above.
+Update any bindings that referenced the old relationship field.
diff --git a/docs/Data/data-sources/airtable.md b/docs/Data/data-sources/airtable.md
index 35251ea8d..df79325de 100644
--- a/docs/Data/data-sources/airtable.md
+++ b/docs/Data/data-sources/airtable.md
@@ -27,8 +27,6 @@ Airtable can be connected to Budibase through some simple configuration. This al
To connect Airtable to Budibase, you need to add a new Data Source. Head over to the data section in the Budibase UI, and click the `Add source` button in the left navigation.
-
-
A dialog will open, allowing you to choose which data source you want to add. Choose Airtable, and click continue.
You will be prompted to enter your API key and the base. You can get your API key from your Account page inside Airtable. To retrieve the `base` from Airtable, head over to the REST API page. This page should list all your bases for you. Click on the base you want to connect, and the `base ID` should be given to you.
@@ -39,16 +37,12 @@ Enter the data into the configuration, and click `Save` to store the details of
To query data from your Airtable base, click the `Add query` button below the Airtable configuration
-
-
First, enter a name for your query, this is purely cosmetical and has no impact on the inner workings. It will be displayed to you when you pick a data source in a [Data Provider](doc:data-provider) in the Budibase Design section.
Choose the function you want to perform. There are detailed instructions on how to complete each function below.
Then there are the specific fields.
-
-
The `Table` field accepts both the table name and the table ID. It is highly recommended to enter the ID as shown above. You can retrieve the ID for your table from the Airtable REST API page.
The `View` field accepts the name of the view you've created in your base. The default is `Grid view` (yes, including a space).
@@ -102,20 +96,12 @@ Bindings in Queries allow you to insert data when using the query. You can use H
Let's take a look at an example where we pass the ID from a binding into the query. Our binding looks like this.
-
-
Then, adjusting our query, we add the `id` using handlebars directly into the string
-
-
Now, wherever you use the created query as a data source in a [Data Provider](doc:data-provider), you can configure the bindings directly from there, using the gearwheel behind the provider selection
-
-
This will then open up the configuration modal, where you can bind the value, for example using a [Form](doc:forms) input.
-
-
## Retrieving relational data
You can have relationships between Airtables, however there is no joining functionality. Instead you must make two REST calls.
@@ -128,28 +114,16 @@ In this example there are many classes with many students.
1. Within Airtable, add a RecordID column to your Students table. This will allow us to filter for the students that attend a particular class.
-
-
2. Also within Airtable, link your Classes table to your Students table.
-
-
3. Add a read query for the class table.
-
-
4. Add a read query for the students table.
-
-
5. In the Design section, add a [Repeater Block](https://docs.budibase.com/docs/blocks#repeater-block) for the Classes query, and a [Table Block](https://docs.budibase.com/docs/blocks#table-block) nested underneath for the Students query.
You can add the class title for each of the student tables, and configure the columns to avoid adding the id strings.
6. Add a filter to the students table block as follows:
-
-
7. The final result should appear as follows:
-
-
diff --git a/docs/Data/data-sources/arangodb.md b/docs/Data/data-sources/arangodb.md
index d179b8be1..da52b2f74 100644
--- a/docs/Data/data-sources/arangodb.md
+++ b/docs/Data/data-sources/arangodb.md
@@ -24,16 +24,8 @@ The only prerequisites for this guide are that you need a Budibase instance, an
The first step is to create your ArangoDB datasource. When in the Data tab in the Budibase builder, click the *Add source* button in the top left corner. A modal will appear allowing you to select what type of datasource you want. Select *ArangoDB* and then click *Continue*.
-
- Select the ArangoDB datasource
-
-
The next step is to add the relevant connection info so that Budibase can connect to your ArangoDB instance. By default the Username will be *root* and the Database Name is \_*system*.
-
- ArangoDB connection info
-
-
If you don't have a Collection yet, go to the URL of your ArangoDB instance - by default this is [localhost:8529](http://localhost:8529/) if you're running it locally - and then sign in to add a new collection.
Once your connection details have been entered, click *Save and continue to query* to save those and test the connection.
@@ -48,10 +40,6 @@ Create queries are very simple in the Budibase ArangoDB integration. They run an
Simply enter the document object into the Fields field. If you want to use Bindings, you can set them up above with a Binding name and a default value. Then in your query, place the binding name where you want it to be used as shown below. We'll see the full power of bindings later in this guide.
-
- Adding a simple document to an ArangoDB collection
-
-
Once you're happy with your query, click *Run Query* to test it. If you are using Bindings the default value for them will be used. This type of query returns the newly created document as a response. You can use a JavaScript function in the [Transformer field](https://docs.budibase.com/docs/transformers) to modify this result, but most of the time the default `return data` will be sufficient. When you're finished working on the query, click *Save Query*.
## Read
@@ -71,10 +59,6 @@ for bag in beans
Using AQL you can add a lot more logic to limit the query or change the structure of the data coming out. In my case here, I'm filtering the results, limiting the count to 10 and sorting them by name.
-
- Additional AQL filtering for our query
-
-
You can also query the database to get a single object where `{{id}}` is a binding like this:
```Text AQL
@@ -93,32 +77,18 @@ To tie this all together, let's build a full CRUD app in Budibase using ArangoDB
We'll start by updating our [Create](#create) query we made above. I'm going to replace the rest of the hardcoded values in the JSON object to use bindings.
-
- Using bindings to create a new document with form values
-
-
Now that we have those bindings in place, we can go to the Design tab to make use of them.
I'm going to start by adding a Blank screen with the URL `/bag/new` where we will create a form for adding a new document to our collection. In my screen I'll add a Form component with a [Field group](doc:field-groups) and a [Button](doc:button). When setting up the form use your Create query as the Schema so that inside the field group we have the relevant fields for each column in the document.
-
- Form component setup
-
-
To make the form fully functional, add an On Click Action to your button to execute the query. In the Execute Query action settings, add the field values from the form for each binding.
-
- Execute Query configuration
-
-
Now whenever you fill in the fields and click the Create button, our Create query will run with the specified values and add a new document to our collection.
## Listing documents
For listing documents we can use the Table block and our simple [Read](#read) query. All you need to do is create a new screen, add a Table block, and choose the query. The table will immediately be populated with data from your collection.
-
-
Once you have this table in place, you can configure the columns however you like. You can easily add a button that will link to the New document screen we built above. You can also route to the Edit document screen that we're building below.
## Updating documents
@@ -153,18 +123,10 @@ When creating our new edit screen, we need to pass in the document's key as a [U
Our edit screen will be set up in a very similar way to our create screen shown above. The main difference is we will use a Repeater block to provide the existing form values as defaults for the field components. We will use the first query we made as the data source for this Repeater block, and use `beans/{{ URL.key }}` as the id binding for that. This query will only provide one document from the collection, but to be extra safe we can also filter the Repeater block by the URL param key and limit it to 1 result.
-
- The edit screen, with form fields populated by the Repeater block
-
-
You will need to configure the Form and Field Group similarly to the ones on the create page, but add the values from the Repeater block as default values for each field. The Form Type should be Update in this case and because we are using custom queries in ArangoDB, we need to use a Custom schema where we define each field variable manually.
The last thing to do when making an edit screen is to hook up the On Click action for the Save button to execute the Update query we made above. In the Execute Query settings we can pass in the form field values to be used in the query bindings, like so:
-
- Our save button's On Click configuration
-
-
## Deleting documents
The last type of query we need to create for a fully functioning CRUD app is one that will delete a document from our collection. This is another query that needs to be set as a Read function so that we can make use of AQL. This time we are using `key` as our binding and will use that to identify the document we want to delete from our collection.
@@ -175,14 +137,6 @@ remove "{{key}}" in beans
Now that we have our query, all we need to do is add a button to our edit screen that will execute this query, passing in the key from our URL parameter for the binding.
-
- Executing the delete query on a button click
-
-
-
- The final edit screen complete with delete button
-
-
> 👍 Nice work
>
> We've successfully built a full CRUD app using Budibase and ArangoDB!
diff --git a/docs/Data/data-sources/csv-import.md b/docs/Data/data-sources/csv-import.md
index 5e0e68041..3a4cac912 100644
--- a/docs/Data/data-sources/csv-import.md
+++ b/docs/Data/data-sources/csv-import.md
@@ -1,6 +1,6 @@
---
title: CSV Import
-excerpt: Importing a CSV or JSON into Budibase as a table
+excerpt: Import CSV or JSON data into Budibase
deprecated: false
hidden: false
metadata:
@@ -10,56 +10,46 @@ metadata:
next:
description: ''
---
-When you already have existing data, you can import this using a CSV file, which will create a new table in the internal [Budibase DB](doc:budibasedb). This will automatically identify columns and create them for you, after which you can specify data types per column.
+Use CSV import when you already have data and want to create a table from it.
-1. Click `Add source` and select `Upload CSV / JSON`.
+Budibase can also import JSON files using the same flow.
-
+## Create a new table
-2. Click `Continue`. You will now see a modal in which you can enter a table name.
+1. Click `Add source`
+2. Select `Upload CSV / JSON`
+3. Upload the file
+4. Enter a table name
+5. Review the detected columns
+6. Choose the data type for each column
+7. Click `Create`
-
+When you import a CSV, the first row is used as the column header row.
-3. Click `Upload` and select the CSV or JSON file you want to import.
+## Import into an existing table
-> 📘 Column names
->
-> When importing a CSV, the first row will be used to determine the column names.
+Use this flow when you want to add rows to an existing Budibase table.
-In the example below, I imported a CSV with 4 columns representing a table of countries. After the file was uploaded, the screen below was shown.
+1. Open the target table
+2. Click `Import`
+3. Upload the CSV or JSON file
+4. Match the source columns to the destination columns
+5. Ignore any columns that do not match
+6. Click `Import`
-
+## Update existing rows
-As you can see, it automatically identified the 4 columns and allowed me to pick the data types for each column.
+You can also update rows from an import.
-4. Click `Create` to build a new table from your imported CSV/JSON file.
+1. Open the import flow for the target table
+2. Enable `Update existing rows`
+3. Select the identifier field
+4. Import the file
-
+Budibase uses the identifier field to match incoming rows against existing records.
-## Importing rows into an existing table
+## Notes
-You may find that you want to update an existing table with new CSV/JSON data. To do so, click on the Budibase table you want to import into. Next click the `Import` button.
-
-
-
-Once you have selected a file, Budibase will match the column names from the CSV against the destination table. Column names that do not match will be ignored, and the modal will highlight the offending fields:
-
-
-
-Click `Import` to pull in the new rows.
-
-## Updating rows with CSV
-
-This is done in a similar fashion to importing a CSV for creating rows. The only difference being, is a toggle at the bottom which needs checked `Update existing rows`. Additionally, an identifier field needs selected. The purpose of this field is to check for records that are already existing. If one is found in the database that matches the CSV. Then this record will be updated with the CSV's data instead of a new record being created.
-
-
-
-> 🚧 Duplicate records
->
-> As an **\_id** field is only assigned upon import, Budibase does not determine if a row has previously been imported. It will always import any rows provided in the CSV/JSON file.
-
-
-
-> 👍 Reading CSV data from an S3 bucket
->
-> If you have a CSV file in an S3 bucket, you can use the [S3](https://docs.budibase.com/docs/s3#read-csv) connector to read the CSV data.
+* CSV imports need a header row
+* JSON imports use the same import flow
+* Rows without a matching identifier are imported as new records
diff --git a/docs/Data/data-sources/google-sheets.md b/docs/Data/data-sources/google-sheets.md
index 87bf403bf..02bfe52d2 100644
--- a/docs/Data/data-sources/google-sheets.md
+++ b/docs/Data/data-sources/google-sheets.md
@@ -1,6 +1,6 @@
---
title: Google Sheets
-excerpt: ''
+excerpt: Connect a Google Sheet as a datasource
deprecated: false
hidden: false
metadata:
@@ -10,104 +10,59 @@ metadata:
next:
description: ''
---
-Google Sheets is a spreadsheet program included as part of the free, web-based Google Docs Editors suite.
+Use Google Sheets when you want Budibase to read and write sheet data directly.
-> 🚧 Google Sheets Compatibility
->
-> The Google Sheets integration only supports simple table structures. For data fetching to work correctly, the sheet must have a clear header row as the first available row.
+Budibase works best with simple tabular sheets. Make sure the first row contains headers.
-## Setup
+## Connect the sheet
-Navigate to the spreadsheet you want to integrate into Budibase. Copy the URL.
+1. Add a datasource and select `Google Sheets`
+2. Sign in with a Google account that can access the spreadsheet
+3. Paste the Google Sheet URL
+4. Select the worksheets to import
+5. Save the datasource
-
+## Work with sheet data
-## Connect
+Each worksheet appears as a grid in Budibase.
-Click the `+` button to add a datasource, and then select 'Google Sheets'.
+You can:
-
+* View rows in the builder
+* Add, edit, and delete rows
+* Use the sheet as a form schema
+* Use row actions such as `Save Row`
-You will then be prompted to sign-in with a Google account. This account should have permissions to view the spreadsheet you want to work with.
+## Add a query
-
+Use a query when you need to create, read, update, or delete rows from a sheet.
-Allow Budibase to access your Google account.
+1. Open the Google Sheets datasource
+2. Click `Add query`
+3. Choose the operation you need
+4. Provide the sheet name
+5. Add the row data or row index required by the query
-
-
-> ❗️ Self-hosting
->
-> If you are self-hosted, you will need to setup [Google SSO](https://docs.budibase.com/docs/sso-with-google)
-
-Once you have authenticated with Google, you will be asked to provide your Google Sheet URL
-
-
-
-Once Budibase successfully connects to your Google Sheet, you can select which worksheets to import (or skip):
-
-
-
-> 🚧 Header row needed
->
-> Your worksheets will need to have a header row to successfully import into Budibase. Your header row must be the first row in each sheet.
-
-## Data view
-
-Each sheet can be viewed and edited as a grid within the Budibase builder.
-
-
-
-You can edit columns, add rows, edit rows and delete rows via the Grid interface. Changes made in Budibase will be reflected in that sheet of your Google spreadsheet. You can also create views that will not be reflected in your Google Sheet, but will help you visualise existing data.
-
-
-
-In addition, Google Sheets data tables can utilise the row actions, such as [Save Row](https://docs.budibase.com/docs/actions#save-row) and be used as a [Form schema](https://docs.budibase.com/docs/forms#form-schema)
-
-## Query
-
-The Google Sheets connector supports adding queries for when you might need to [transform data](https://docs.budibase.com/docs/transformers) or perform CRUD operations with specific [bindings](https://docs.budibase.com/docs/custom-queries#using-query-bindings).
-
-To add a query, select your Google spreadsheet datasource, and click `Add query` at the bottom of the configuration page.
-
-
+## Query types
### Create
-Allows you to insert a row. You must provide the name of the sheet as well as a JSON payload to specify the column names and value.
-
-
-
-The row number of the created entry will be returned on success.
-
-
-
-
+Insert a new row by providing the sheet name and a JSON payload of column names and values.
### Read
-Simply returns all the rows of a specified sheet. The transformer allows you to filter and map the data into the schema you desire.
-
-
+Return all rows from a sheet. Use a transformer if you need to filter or reshape the data.
### Update
-Similar to *Create* in that you must provide a payload of data, however you must also provide a row index to be overwritten.
-
-> 📘 Row index
->
-> Row indexing starts from zero - the first row below the header row (usually the second row).
-
-
-
-
+Update an existing row by providing the sheet name, row index, and new values. Row indexes start at zero for the first data row below the header.
### Delete
-The same as *Update*, a sheet name and row index must be provided. That row will be deleted.
-
-
+Delete a row by providing the sheet name and row index. Rows below the deleted row shift up.
-Rows will be shifted up after deletion.
+## Notes
-
+* Your sheet must have a header row
+* Self-hosted installations need Google SSO configured
+* Use exact sheet and column names to avoid import errors
diff --git a/docs/Data/data-sources/index.md b/docs/Data/data-sources/index.md
index 72b3eb2e9..9155f0522 100644
--- a/docs/Data/data-sources/index.md
+++ b/docs/Data/data-sources/index.md
@@ -12,115 +12,44 @@ next:
---
Budibase can store your data inside the [native internal database](doc:budibasedb) (CouchDB). This is the default datasource unless you opt to connect another datasource to Budibase.
-
- List of app datasources
-
-
Budibase supports connection to a range of remote datasources. More information about connecting Budibase to each of these datasources is nested in this section of the documentation.
Furthermore, if a datasource you want to use is missing, it is possible to build a [custom datasource](https://docs.budibase.com/docs/custom-datasource).
## Schema
-When [Fetching tables](https://docs.budibase.com/docs/sql-datasource#fetching-tables) from external SQL databases, Budibase will attempt to match the external data types to the internal [Budibase DB](https://docs.budibase.com/docs/budibasedb) types. These default types can be changed by clicking the three dots icon beside the table column, and selecting `Edit column`:
-
-
- Editing a column type, demonstrated on an MS SQL database
-
-
-
- Column types displayed in a dropdown when creating a new column for the internal Budibase DB
-
-
-For other basic datasources and [Custom queries](https://docs.budibase.com/docs/data-sources#custom-queries), the schema will be determined by selecting types in the query output.
+When [Fetching tables](https://docs.budibase.com/docs/sql-datasource#fetching-tables) from external SQL databases, Budibase matches the external data types to the internal [Budibase DB](https://docs.budibase.com/docs/budibasedb) types.
-
+For other basic datasources and [Custom queries](https://docs.budibase.com/docs/data-sources#custom-queries), the schema is determined by the types in the query output.
The schema is important, as it will affect how [sorting and filtering](https://docs.budibase.com/docs/data-provider#pagination-and-sorting) is applied in the design section.
## Sample data
-Budibase provides the option of adding some sample data to allow new users to explore the various [Data types](https://docs.budibase.com/docs/budibasedb#data-types) available, and quickly build an app for learning purposes. While it is an internal Budibase DB source, it will be kept separate to the other internal DB tables that you create.
-
-
- Use sample data
-
+Budibase includes sample data so new users can explore the available [data types](https://docs.budibase.com/docs/budibasedb#data-types) and build an app quickly. It stays separate from the internal DB tables you create.
If you do not need this data, it can easily be removed in the same way as other additional datasources. Simply click on the ellipsis to the right of the sample data source and click *Delete*.
-
- Deleting sample data
-
-
## Security
-When connecting to an external datasource you must allow Budibase access. Normally datasources will be protected by a firewall and you will need to open firewall access to your datasource from the Budibase cloud or from your self-hosted Budibase. Port numbers and user management differ across each type of datasource so if you have difficulty connecting you should speak to the Database Administrator for your organisation.
-
-
-
-***
+When connecting to an external datasource you must allow Budibase access. Datasources are usually protected by a firewall, so you may need to whitelist the Budibase cloud or your self-hosted Budibase server.
-
+## Custom queries
-# Custom queries
+Budibase supports custom queries for advanced joins and server-side filtering.
-Budibase supports the ability to write custom queries. These queries can be really useful if you want to perform advanced joins or server-side filtering.
-
-To write custom queries, head over to your Datasource, and scroll down until you find the queries section. Click the `Add Query` button, which will guide you to a form to configure your query.
+To write one, open a datasource and go to the **Queries** section.
> 📘 Budibase DB
>
-> The internal Budibase DB is a unique datasource that does not support custom queries.\
-> It does however support [Views](https://docs.budibase.com/docs/views)
-
-An example would be to query all pet names from a specific owner. For this demonstration we've used a custom binding to insert the `ownerID` into the query.
-
-
-
-> 📘 Read or Write
->
-> If you want to use a custom query with a [Data provider](doc:data-provider), you must select **Read** as the function of your query.
->
-> Other query types, such as Create, can be used by the [Execute Query action](https://docs.budibase.com/docs/data-sources#tutorial-posting-form-data-to-a-custom-query).
-
-## Using query bindings
-
-You can also override the default binding values.
-
-When you use the query as a data source for your provider, you can click on the cog icon.
-
-
-
-This will show a query bindings drawer where you can add a dynamic value to override the default.
-
-
-
-## Editing custom queries
-
-A list of your custom queries can be found underneath the associated data sources. Click on the query to be able to edit the config.
-
-It is also possible to **delete** queries that are no longer needed, or **duplicate** queries, which can useful in cases where an additional query only requires minor changes.
-
-To access these options, hover over the query and click the **…**
-
-
-
-## Tutorial: Posting form data to a custom query
-
-1. Add your query to a datasource, in this example we'll use [PostgreSQL](https://docs.budibase.com/docs/postgresql-1). Add your bindings with some optional defaults, and then reference them within your query.
-
-
-
-2. Add a [Form](doc:forms) with fields for each of the bindings, except for the *id* which will come from the [URL ID](https://docs.budibase.com/docs/url-parameters) binding.
-
-
+> The internal Budibase DB does not support custom queries. It does support [views](https://docs.budibase.com/docs/views).
-3. **Define actions** for the 'Submit' button. Choose the **Execute Query** action and pick *PostgreSQL* as the datasource. You should now be able to see and select your custom query.
+Use `Read` queries with a [Data provider](doc:data-provider).
-
+Use `Execute Query` for create, update, or delete actions.
-4. Finally map your form field bindings to your custom query bindings like so:
+Add bindings when the query needs dynamic values, and use the query settings to override defaults at runtime.
-
+You can edit, duplicate, or delete queries from the datasource list.
-Click **Save** to finish.
+For the standard SQL workflow, see [Working with SQL datasources](doc:sql-workflow).
diff --git a/docs/Data/data-sources/mongodb.md b/docs/Data/data-sources/mongodb.md
index 6d48fffac..cc6054967 100644
--- a/docs/Data/data-sources/mongodb.md
+++ b/docs/Data/data-sources/mongodb.md
@@ -10,7 +10,7 @@ metadata:
next:
description: ''
---
-MongoDB is a modern NoSQL database, that can be connected to Budibase with a few clicks.
+MongoDB is a modern NoSQL database that can be connected to Budibase with a few clicks.
Before we get started, make sure you've got the following:
@@ -34,16 +34,8 @@ Now that you have everything you need to hook up your MongoDB installation to Bu
To connect your MongoDB Data Source, head over to the data section in the Budibase builder UI. Then, click the `Add source` button in the left-hand settings panel to add a new data source.
-
- Press the `+` icon to add a data source
-
-
You will be presented a dialog, in which you choose `MongoDB`. Then you will be presented with the connection configuration.
-
- A standard connectionstring when connecting to MongoDB Atlas
-
-
Paste your ConnectionString, and enter the database name which you want to connect with, and click `Save and continue to query`.
Pressing this button will save the ConnectionString into your Budibase installation.
@@ -58,12 +50,10 @@ Now that you have added the connection to your database, you can start querying
The first step is to click the 'Add query' button, which is located on the data source page you just added. If you're not on this page, click on the MongoDB connection in the list of data sources on the left.
-
+Choose the query name, collection, function, and access level first. Then use the action type to define which MongoDB operation Budibase should run.
### Query configuration
-
-
Configuring your query is essential in making sure it runs correctly. In the table below you'll find what each field means.
| Field | Description |
@@ -84,12 +74,10 @@ You have the option to insert one or many documents into a collection.
When inserting a single document, provide a payload within the query box.
-
-
In this case, an ObjectId is automatically generated for the new document.\
-If you want to specify an ObjectId, you can do that as follows:
+If you want to specify an ObjectId, you can do that as follows:
-
+If you are inserting multiple documents, provide an array of objects instead of a single object.
> 🚧 Document ID
>
@@ -97,18 +85,12 @@ If you want to specify an ObjectId, you can do that as follows:
When inserting many, you must provide an array of objects.
-
-
### Read query
The most basic query you can do is a *find* with no query body.\
This will return all data available within the specified collection.
-
-
-A filter object can be added to the query box. For example, get all documents in the collection where age is greater than 40:
-
-
+A filter object can be added to the query box. For example, return only documents where age is greater than 40.
A full list of query operators can be found here: [https://www.mongodb.com/docs/manual/reference/operator/query/](https://www.mongodb.com/docs/manual/reference/operator/query/)
@@ -116,13 +98,11 @@ A full list of query operators can be found here: [https://www.mongodb.com/docs/
When performing an update you must provide two objects:
-1. **uired]** F** Filter - find the documents to change.
-2. **uired]** U** Update - instructions for the modifications to make.
-3. **ional]** O** Options - additional update parameters (more [here](https://www.mongodb.com/docs/v4.4/reference/method/db.collection.updateOne/))
+1. **Required** Filter - find the documents to change.
+2. **Required** Update - instructions for the modifications to make.
+3. **Optional** Options - additional update parameters (more [here](https://www.mongodb.com/docs/v4.4/reference/method/db.collection.updateOne/))
-For example, set the *retired* property to true for all documents that have an age over 65:
-
-
+For example, set the *retired* property to true for all documents that have an age over 65.
If *updateOne* was used instead, then only the first filter match would have been updated.
@@ -133,9 +113,7 @@ A full list of update operators can be found here: [https://www.mongodb.com/docs
To delete ALL records from a collection, run a query with no filter and the *deleteMany* action type set.\
**Careful!**
-Otherwise, you can add a filter to delete all matching records:
-
-
+Otherwise, you can add a filter to delete all matching records.
If *deleteOne* was used instead, then only the first filter match would have been deleted.
@@ -151,19 +129,11 @@ Bindings in Queries allow you to insert data when using the query. You can use H
Let's take a look at an example where we pass the ID from a binding into the query. Our binding looks like this.
-
-
Then, adjusting our query, we add the `id` using handlebars directly into the string
-
-
-Now, wherever you use the created query as a data source in a [Data Provider](doc:data-provider), you can configure the bindings directly from there, using the gearwheel behind the provider selection
-
-
-
-This will then open up the configuration modal, where you can bind the value, for example using a [Form](doc:forms) input.
+Now, wherever you use the created query as a data source in a [Data Provider](doc:data-provider), you can configure the bindings directly from there using the gearwheel behind the provider selection.
-
+This will then open up the configuration modal, where you can bind the value, for example using a [Form](doc:forms) input.
### Transformer
@@ -179,19 +149,15 @@ The `transformer` box accepts JavaScript, and is ran within a function. This mea
Now that you have configured the entire query correctly, it's time to save the query. You can't save the query without running it first so we know it actually is working.
-So the first thing you want to do now, is click the `Run Query` button
-
-
+So the first thing you want to do now is click the `Run Query` button.
As soon as you click the button, the query will be executed. When the query is successful, the result will be displayed below. There you can inspect the result, configure the schema and preview it in a table.
-
-
### Configuring schema
Configuring the schema is important for Budibase, as it allows the interface to understand what kind of data it is dealing with. In order for you to configure the data, click the `Schema` Tab, which is next to the `JSON` tab in the results section. You should then be presented with a page like this:
-
+Use this view to confirm the shape of the data before you save the query.
## Aggregations
@@ -201,15 +167,13 @@ This is done through *pipelines* which contain a number of *stages*. More from t
To use aggregations within Budibase, select the **Aggregate** function.
-
-
There are two modes in which you can interact with aggregations: JSON and Pipeline.
### Json
This is the default mode. Simply enter an array of stages to be processed.
-
+Use this when you already know the pipeline and want to paste it directly.
### Pipeline
@@ -217,8 +181,6 @@ Whilst the JSON mode can do everything you need, you may find the GUI mode makes
Add as many stages as you like with the `+` button, delete stages with the trashcan icon, and use the arrows to swap stages.
-
-
A benefit of using the Pipeline mode is that it provides the available aggregation stages in a dropdown, and selecting a stage will fill out a template of relevant properties, which can be easily altered.
***
@@ -237,35 +199,11 @@ A benefit of using the Pipeline mode is that it provides the available aggregati
In MongoDB it is common to use nested documents instead of relationships. For example, this **transactions** collection returns an array of transaction meta data, with nested transaction data:
-
-
-Looking at the `Schema` tab, we can see a property called *transactions* of type *Array*:
-
-
- Array of transactions
-
-
-When displaying nested array query data, Budibase will automatically generate the schema for the nested objects.
+Budibase will generate schema for the nested objects automatically once the query returns the data.
To make use of the nested data, first add a Repeater [Block](doc:blocks) to your screen for the query.
-
- Selecting the query
-
-
-Next nest a [Table block](doc:table-block) under the repeater block. The nested data source can be selected under the *JSON Arrays* section:
-
-
-
-Alternatively you can nest another **Repeater block**, which would give you access to the nested data row [bindings](doc:introduction-to-bindings):
-
-
-
-The data types are determined automatically based on the primitive types provided by the Mongo query:
-
-
- Example nested transaction data in Mongo Atlas
-
+Next nest a [Table block](doc:table-block) under the repeater block. Select the nested array from the *JSON Arrays* section of the table block.
@@ -275,40 +213,16 @@ In the previous example we had a nested array of objects. Sometimes you will hav
In this tutorial you will see how to display a list of product tags underneath each account. Here is the result of the Mongo query:
-
-
-
- Account schema with products array
-
+The pattern is the same: use one repeater for the parent records, a second repeater for the nested `products` array, and a Tag component inside that repeater.
To begin create your screen and add a **Repeater block**. Select the *Read accounts* query.
-
-
-Next add a [Headline](https://docs.budibase.com/docs/displaying-text#headline) and display the *account\_id* with the following Binding: `Account ID: {{ Accounts Repeater Block.Read accounts.account_id }}`
-
-Now we'll nest a second **Repeater block**, this time for the associated account products. Under the *Fields* section of the *Data* setting, select the *products* field:
-
-
- Selecting the related account products
-
-
Under the second repeater block, nest a **Tag** component. Your final Component Tree should look roughly like so:
-
- Final component tree
-
-
In the Settings Panel, set the *Text* property of the tag to the products value: `{{ Products Repeater Block.products.value }}`
-
- Select the 'Value' binding from the products repeater
-
-
Use [Conditional UI](doc:conditions) to give the tags a custom colour based on the product:
-
-
#### Result
-
+The final screen shows each account with its related product tags styled by value.
diff --git a/docs/Data/data-sources/ms-sql-server.md b/docs/Data/data-sources/ms-sql-server.md
index 22b21346e..697489785 100644
--- a/docs/Data/data-sources/ms-sql-server.md
+++ b/docs/Data/data-sources/ms-sql-server.md
@@ -1,6 +1,6 @@
---
title: MS SQL Server
-excerpt: Connecting Budibase to an existing Microsoft SQL Server
+excerpt: Connect Microsoft SQL Server as a datasource
deprecated: false
hidden: false
metadata:
@@ -10,210 +10,36 @@ metadata:
next:
description: ''
---
-> 👍 SQL Databases
->
-> Budibase is designed to integrate with any core SQL database.
->
-> Read more about [SQL Datasources](doc:sql-datasource).
+Use MS SQL Server when you want Budibase to connect to an existing Microsoft SQL Server database.
-# Connecting to SQL Server
+Budibase supports SQL Server 2016 and later.
-After clicking on the SQL Server datasource you will be prompted to enter connection details:
+## Connect the datasource
-
+1. Open the **Data** section.
+2. Add a new source.
+3. Choose `MS SQL Server`.
+4. Enter the connection details.
+5. Fetch the tables you want to use.
-## Advanced authorisation
+If Budibase is running in Docker and the database is on the same machine, use `host.docker.internal` or `172.17.0.1` on Linux.
-If you are using an MS SQL Server protected via Azure Active Directory, you can setup the `Advance auth` field to `Active Directory`, providing the required secrets.
+## Temporal tables
-
+Budibase supports system-versioned temporal tables. It imports the main table, excludes history tables from fetch, and marks system-managed period columns as auto-columns.
-The Azure documentation can be found [here](https://learn.microsoft.com/en-us/sql/relational-databases/security/authentication-access/azure-ad-authentication-sql-server-setup-tutorial?view=sql-server-ver16).
+## Fetch tables
-
+Fetch only the tables you need. You can fetch additional tables later if the schema changes.
-***
+## Define relationships
-## Temporal Tables
+Add relationships after you fetch the tables.
-Budibase supports MSSQL [System-Versioned Temporal Tables](https://learn.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables). When you fetch the schema for an MS SQL Server database, Budibase handles temporal tables as follows:
+Use one-to-many relationships for foreign keys and many-to-many relationships when your schema includes a join table.
-* **Primary Table**: The main temporal table is imported like any other base table.
-* **History Table**: Budibase automatically detects and excludes history tables from the fetch process. This prevents your data schema from being cluttered with read-only history entities.
-* **Generated Columns**: System-managed columns used for period definitions (such as `ValidFrom` and `ValidTo`, often defined as `GENERATED ALWAYS AS ROW START/END`) are automatically detected and marked as **Auto-columns** in Budibase. This ensures the table remains writable while respecting the system-managed nature of these specific fields.
+## Custom queries
-
+Use custom queries for joins, grouped results, or write actions from a form or button.
-***
-
-
-
-# Tutorial: Building a Bike Store app
-
-In this tutorial we will look at building a Budibase app from scratch using an existing Microsoft SQL Server database as our foundation. Our data is split between two schemas.
-
-One schema is called **Production** which contains our products, details and stocks. This data will be available to the public for customer use.
-
-The second schema is called **Sales** which contains internal data to be seen and managed by employees only.
-
-The full dataset can be found on [sqlservertutorial.net](https://www.sqlservertutorial.net/sql-server-sample-database/).
-
-## Setting up the datasources
-
-Given you have an MS SQL Server instance running, with the dataset committed, we can begin pulling that data into Budibase.
-
-First create an app - no sample data needed.
-
-
-
-As we have two schemas, we will want to create two [Datasources](doc:data-sources); one for each schema.
-
-Click `Add source` and select `MS SQL Server`. Fill out the connection details and press `Save and fetch tables`.
-
-
-
-Repeat the same process, but change the *Schema* to **production**.
-
-By default the datasource names will be *SQL Server* and *SQL Server-2*. It is best to rename these to match the corresponding schemas.
-
-For example, select the datasource for the **production** tables, change the name under configuration, and click `Save`. Do the same for **sales**.
-
-
-
-Your final *Sources* tree should look as follows:
-
-
-
-## Define existing relationships
-
-Now that we have pulled in our tables, we need to inform Budibase how the tables relate to each other.
-
-* One Brand -> Many Products
-* One Category -> Many Products
-* One Store -> Many Staff
-* One Store -> Many Orders
-* One Staff -> Many Orders
-* One Customer -> Many Orders
-
-To get started, click on the `brands` table. Next click on the `Define existing relationship` button along the top of the table.
-
-
-
-This will pop up a dialog for you to select the `products` table as the *to table*, and select the *brand\_id* as the foreign key.
-
-
-
-Click `Save` and you will see the relationship links successfully added in both tables.
-
-
- Product links in the brands table
-
-
-
- Brand links in the products table
-
-
-The same process can be followed for the other desired relationships mentioned. Alternatively you can [define relationships](https://docs.budibase.com/docs/sql-datasource#defining-relationships) from the datasource configuration page.
-
-## Adding a public products catalogue screen
-
-In this tutorial we want to allow customers to browse the available products of the bike store. First we must give public users permissions to view the data. For each table under the **Production** datasource, click on `Manage access`.
-
-
-
-This will display a modal for setting the read and write access levels. As we only want the public to be able to read the data, select the **Public** role for *Read* and click `Done`.
-
-
- Anyone can read the products, but write require authorization
-
-
-> 💡 Manage access for relationships
->
-> When making data public, you must also give the related data public access.\
-> In this case, `products`, `brands` and `categories` must all have public read access.
-
-With the data permissions now set, we can add our screen into the [Design](doc:working-with-data) section. Click on the **Design** tab, and select the `/home` screen. Under access, select **Public** so that anyone can view the [Screen](doc:screens).
-
-
- Public screen access
-
-
-Next click on `Add component` and add a [Cards block](https://docs.budibase.com/docs/blocks#cards-block). Select `products` as the datasource, and fill out the cards title, subtitle and description. Use the Binding from the dropdowns to pull data from the SQL datasource.
-
-
- Selecting the product name as the card title
-
-
-Bindings also allow you to combine static text with dynamic data pulled from your SQL table. For example, we can prefix the price with a dollar sign using the following handlebars expression:
-
-```
-${{ Products Cards block.products.list_price }}
-```
-
-
-
-You can tick *paginate* and adjust the *limit* setting to determine how many products should be displayed per page.
-
-Of course given a catalogue of products, users would like to be able to filter. You can easily add a *Search column* via the dropdown in the cards block.
-
-
- Adding search columns
-
-
-For example, you may want to allow users to search by product name, brand name, or model year.
-
-
-
-It would also be nice to add in some tags for the brand and category of the product. To do this let's [eject the block](https://docs.budibase.com/docs/blocks#ejecting-blocks). This will give us a more granular component tree like so:
-
-
- Ejected cards block
-
-
-With access to the [Repeater](doc:repeater), we can now add a couple of [Tag](doc:tag) components below the *Spectrumcard*; one each for the brand and category.
-
-Use the following binding for the tag text: `{{ Repeater.products.brands.0.primaryDisplay }}`
-
-To explain, *brands* is the name of the relationship field. The `.0` is required as relationship fields are always considered to be an array, however in this case we know that it will only ever be one element. Finally the `primaryDisplay` corresponds to the [table display column](https://docs.budibase.com/docs/budibasedb#selecting-the-display-column).
-
-Using [Containers](doc:container) and a bit of styling, you can create something like this:
-
-
-
-Finally click the `Preview` button in the top-right corner of the screen to test the app so far.
-
-Select `View as public user` to make sure unauthenticated users can view the data.
-
-
-
-## Managing sales data
-
-Whilst we want the public to be able to view the available products, the sales data should be managed by authenticated users.
-
-After setting up your relationships, as outlined earlier in the tutorial, go to the **Design** section and click `Add screen`. Next click on `Autogenerated screens`.
-
-This will prompt you to select the table to create the [Autogenerated screen](https://docs.budibase.com/docs/table-block#autogenerated-screen) for. Select the `orders` table and `Confirm`.
-
-
-
-Next you will be prompted to select an access level. By default **Basic** will be selected, which requires an authenticated user of the app. Click `Done`.
-
-This will add full CRUD functionality for the `orders` table. If you want to customise this screen, look at the [Table block](doc:table-block) page for more info.
-
-
- Basic logged in user can view and edit orders
-
-
-Whilst public users will be denied access to the *Orders* screen, it would be better to not show them the [Navigation](doc:navigation) link so as to avoid confusion.
-
-Click on the navigation panel, and click `Configure links`. Next beside `/orders` select a *Minimum role* - in this case **Basic**. Click `Save`.
-
-
-
-This will ensure that public users do not see the *Owners* link.
-
-
- Unauthenticated user only sees the Home link
-
-
-Auto-generated screens can be created for the other **sales** tables, but different [access levels](https://docs.budibase.com/docs/user-roles#app-specific-roles) could be used.
+See [Working with SQL datasources](doc:sql-workflow) for the shared workflow after the connection is in place.
diff --git a/docs/Data/data-sources/mysql-mariadb.md b/docs/Data/data-sources/mysql-mariadb.md
index e9beb317d..76db9e013 100644
--- a/docs/Data/data-sources/mysql-mariadb.md
+++ b/docs/Data/data-sources/mysql-mariadb.md
@@ -1,6 +1,6 @@
---
title: MySQL / MariaDB
-excerpt: Using a MySQL Database Server as a datasource for Budibase
+excerpt: Connect a MySQL or MariaDB database as a datasource
deprecated: false
hidden: false
metadata:
@@ -10,345 +10,37 @@ metadata:
next:
description: ''
---
-> 👍 SQL Databases
->
-> Budibase is designed to integrate with any core SQL database.
->
-> Read more about [SQL Datasources](doc:sql-datasource).
+Use MySQL / MariaDB when you want Budibase to connect to an existing relational database.
-# Video tutorial
+Budibase supports:
-
+* MySQL 8.0 and later
+* MariaDB 10.6 and later
-
+## Connect the datasource
-***
+1. Open the **Data** section
+2. Add a new source
+3. Choose `MySQL / MariaDB`
+4. Enter the connection details
+5. Fetch the tables you want to use
-# Tutorial: Building a HR portal
+If Budibase is running in Docker and the database is on the same machine, use `host.docker.internal` as the host value.
-In this tutorial we will look at building a Budibase app from scratch using a MySQL employees sample database.
+## Fetch tables
-The full dataset and installation instructions can be found on [GitHub: datacharmer/test\_db](https://github.com/datacharmer/test_db).
+Fetch only the tables you need. You can fetch more tables later if the schema changes.
-
+## Define relationships
-## Adding the datasource
+Add relationships after you fetch the tables.
-When you have a MySQL or MariaDB Server instance running, with the sample database installed, we can begin pulling that data into Budibase.
+Use one-to-many relationships for foreign keys and many-to-many relationships when your schema includes a join table.
-First create a new Workspace. We will set up our MySQL datasource and app here.
+If Budibase rejects a join table, update the source table so the foreign key columns are nullable, then fetch the table again.
-
+## Custom queries
-Once the new workspace has been created, click `Add source` and select `MySQL`, even if you are using MariaDB, or any other storage engine.
+Use a custom query when you need joins, grouped results, or write actions from a form or button.
-
-
-Click `Save and fetch tables` to finish.
-
-
-
-## SSL
-
-You have the option to provide an SSL which prompts you with two additional fields `key` and `value`. The ssl `key` in the connection options takes a string. There are a lot of different options that can be used here, the 3 main ones are listed below.
-
-* `ca:` The certificate(s) to trust instead of the ones Node.js is configured to trust. This refers to the value of the certificate(s) and not a filename of the certificate(s).
-* `cert:` The client certificate to use in the SSL handshake.
-* `ciphers:` The ciphers to use to use in the SSL handshake instead of the default ones for Node.js.
-
-Additional information on other keys or more information on the 3 listed above can be found [here](https://github.com/mysqljs/mysql#ssl-options).
-
-The `value` is the actual content of the SSL certificate files, and the paths provided in the MySQL configuration are pointers to these files. `/path/to/private-key.pem`
-
-## Define existing relationships
-
-Now that we have pulled in our tables, we need to inform Budibase how the tables relate to each other.
-
-* One Employee -> Many Titles (Historical)
-* One Employee -> Many Salaries (Historical)
-* Many Departments -> Many Employees / Managers
-
-To define these relationships, click on the MySQL datasource in the datasource list and scroll down to and click `Define relationship`.
-
-
-
-For the one employee to many titles and salaries, define a relationship like so:
-
-
- One employee -> Many titles (matching on emp\_no)
-
-
-For the relationship between **departments** and **employees**, there are joining tables, e.g. **dept\_emp**. Usually we would setup a [many-to-many relationship](https://docs.budibase.com/docs/sql-datasource#many-to-many) using the joining table as the *Through* table. Unfortunately in this case, the joining tables do not meet the requirements outlined by Budibase:
-
-
- dept\_emp is not a suitable Budibase joining table
-
-
-
- from\_date and to\_date are invalid
-
-
-Luckily, this is an easy fix. Within MySQL Workbench, or through an alter SQL statement, make the **from\_date** and **to\_date** fields *nullable* for both joining tables.
-
-```sql
-ALTER TABLE `employees`.`dept_emp`
-CHANGE COLUMN `from_date` `from_date` DATE NULL ,
-CHANGE COLUMN `to_date` `to_date` DATE NULL ;
-```
-
-
- dept\_emp is now a suitable joining table
-
-
-Adding the Many -> Many relationships for **dept\_emp** should now work.
-
-> 📘
->
-> Make sure to re-fetch the tables to pull down the new schema information!
-
-
- Many Departments -> Many Employees
-
-
-An additional Many -> Many relationship cannot be added for two tables that already have a Many -> Many relationship established, but for this tutorial we do not need the **dept\_manager** table.
-
-Because the Many -> Many relationship between **departments** and **employees** will pull down a huge amount of information, we will also create a Custom SQL query called *Get Departments Only* to simply get the departments without any employee data when needed.
-
-
- Simple select statement with no relationship data
-
-
-In the end your relationships table should look something like so:
-
-
-
-By default the display name for the relationships will be the ID, but this isn't very readable:
-
-
-
-To improve upon this, we can [select a display column](https://docs.budibase.com/docs/budibasedb#selecting-the-display-column) for each table. This will be the column that appears in the relationship pills. As an example, for the **employees** table, open the menu on the **first\_name** column and select `Use as table display column`.
-
-
- Setting the first\_name to be the display column
-
-
-The employee relationship in the related tables, such as **titles**, is now more human readable. You can also use [Formula](doc:formula) columns to combine the first and last names of the employee into a single display column.
-
-Create a new formula column in the employees table called 'full\_name' with the following binding expression: `{{ first_name }} {{ last_name }}`. Set this column as the table display column.
-
-
-
-
-
-## Org chart screen
-
-Now that we have the data pulled into Budibase, let's create a screen that allows HR to see the various departments and the org structure for each.
-
-### Step 1 - Adding the row explorer
-
-On the home screen, click `Add component` and select the *Row Explorer Block*. For the table select **departments**, and remove the height - we will display all nine departments at once without the need for scroll.
-
-Next in the *Cards* section, set the *Title* to match the **dept\_name**, and remove the *Subtitle* and *Description*.
-
-
- Selecting the department name as the card title
-
-
-
- List of clickable department names
-
-
-#### (Optional) Adding icons for each department
-
-To add a little bit of flavour to the row explorer, we can provide an *Image URL* for each department. One way to do this would be to host the images in a public repository and name the files to match the department names. Another approach is to create a column in your table to store a URL icon path, which is what we'll do in this example.
-
-1. Within Budibase, click on the **departments** table, and click `Create column`.
-2. Add a [Text](doc:text) column called **icon\_url**.
-3. For each department row, add a URL path to an appropriate icon. I have found [svgrepo.com](https://www.svgrepo.com/) to be a good resource for free SVG icons. For your convenience, I have provided SQL update statements:
-
-```mysql
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/428772/marketing-market-social.svg' WHERE dept_no = 'd001';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/381027/finance-bank-piggy-business-money.svg' WHERE dept_no = 'd002';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/218257/human-resources-search.svg' WHERE dept_no = 'd003';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/430190/production-industrial-automation.svg' WHERE dept_no = 'd004';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/428736/development-cognitive-progress.svg' WHERE dept_no = 'd005';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/422994/quality-premium-certificate.svg' WHERE dept_no = 'd006';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/430192/sales-shop-analytics.svg' WHERE dept_no = 'd007';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/119008/research.svg' WHERE dept_no = 'd008';
-UPDATE departments SET icon_url = 'https://www.svgrepo.com/show/429955/customer-service-hotel.svg' WHERE dept_no = 'd009';
-```
-
-4. In the Design section, click on the Row Explorer block and set the Image URL to the **icon\_url** field.
-
-
- Selecting an image url
-
-
-5. The image icons are not in the correct aspect ratio or a consistent size, but we can use the following [Global styling](https://docs.budibase.com/docs/custom-css#global-styling) to format the background image as needed:
-
-```html
-
-```
-
-6. We also do not need the search, so we should [Eject](https://docs.budibase.com/docs/blocks#ejecting-blocks) the block and delete the form. Your builder should appear like so:
-
-
-
-### Step 2 - Adding the manager and employee cards
-
-After ejecting the row explorer block, we need to change the Data provider source to the *Get Departments Only* as we do not want the relational employee data in this simple list of departments.
-
-In addition, the auto-generated click action of the `Spectrumcard` will need to be updated. Click `Define actions` and change the state value to the *dept\_no* from the query:
-
-
-
-At the moment upon previewing the app, a basic form is shown on click of each department row. Instead we want to display a card for the department managerial team and their employees.
-
-First delete the [Form block](doc:form-block) and button components from the container. Next we want to make sure the clicked department name is available in the explorer panel. To do this, we will push the clicked row department name into [App state](doc:app-state).
-
-Click on the `Spectrumcard` component and within the Settings Panel click `Define actions`. Then click `Add Action` -> `Update State` and set a variable called *DepartmentName* as follows:
-
-
-
-Be sure to click the `Save` button!
-
-Whilst we could use multiple nested repeaters to pull in all of the department detail we want to display, it is much more efficient to use a [Custom SQL query](doc:custom-queries). Use the following SQL:
-
-```sql
-SELECT * FROM departments d
-JOIN dept_manager dm on d.dept_no = dm.dept_no
-JOIN employees e on dm.emp_no = e.emp_no
-JOIN titles t on t.emp_no = e.emp_no
-WHERE d.dept_name = {{ dept_name }} AND (e.emp_no, t.from_date) in (select t.emp_no, max(t.from_date) from titles t group by emp_no)
-```
-
-Also add a binding for the **dept\_name**, and give the query the name *Get Managers by Department Name*. Make sure to save!
-
-
-
-Back to the Design section, add a [Cards Block](https://docs.budibase.com/docs/blocks#cards-block) for the managers using the SQL query datasource just added, passing in the binding: `{{ State.DepartmentName }}`. This will replace the form block we removed.
-
-
-
-Within the *Cards* section set the *Title* to `{{ Managers.Get Managers by Department Name.first_name }} {{ Managers.Get Managers by Department Name.last_name }}`.
-
-For the *Description*, we want to display the managers current (most recent) job title: `{{ Managers.Get Managers by Department Name.title }}`
-
-With the card block setup, it is also worth adding a headline component to indicate which department has been selected. Above the cards block, add the headline component with the title `{{ State.DepartmentName }}`. Add some styling such as margin bottom and *Extra Large* size.
-
-A preview of the app should show the following so far:
-
-
-
-
-
-## Employees screen
-
-As this data set has thousands of employees per department, cards are not an ideal way to display the data. Instead we can display this data in a paginated [Table block](doc:table-block) filtered on an encoded department ID passed via [URL variables](doc:url-parameters).
-
-To get started, click `Add screen` and select `Autogenerated screens`. Tick the **employees** table and confirm.
-
-Next we want to be able to pass through the **emp\_no** and **dept\_name** from the home screen. To support this, configure the screen route as follows:
-
-
- Employees screen with two variables
-
-
-Use the name URL variable in the *Title* of the table block.
-
-
- Access the department name from the URL
-
-
-With so many records, search will be very important. Under *Search Fields* select some searchable fields.
-
-
-
-Now let's make use of the `{{ URL.id }}` binding to filter our employees by department. `Define filters` of the table block. The **dept\_no** will be available via the **dept\_emp** relationship, and we can match that against the ID in the URL as follows:
-
-
-
-Also set the *Scroll Limit* of the table block to 50.
-
-We will also want to `Configure columns`. Click on `Add all columns` and then press the `x` beside the **departments** column. We don't want to display the relationship because it will be the same for all rows.
-
-### Linking from the home screen
-
-The employee screen is ready to go, but we still need to link it to the home screen.
-
-Add a [Link](doc:link) component underneath the *Managers* card block. Add some margin top for some space. Give the URL the following binding: `/employees/{{ State.cAF3fs8fNJ }}/{{ State.DepartmentName }}`. The ID state will be different in your case.
-
-
-
-
-
-## Salary history
-
-The **salaries** table holds a historic record of each employees salary at a particular time. This time series can be displayed in a chart.
-
-Eject the Table block in the *employees* screen.
-
-Within the newly exposed *Details side panel*, select the *Details form block* and deselect **departments** and **salaries** from the *Fields* dropdown. We already know the department from the table heading, and we will be displaying the salaries in a chart instead of the [Multi-select picker](doc:multi-select-picker).
-
-
-
-We also want to make sure that we pass the correct ID through to state when clicking on a row. Click on the Table underneath the Data provider, and click `Define actions` under the *On Row Click* section of the Settings Panel.
-
-By default you will see the value binding of `{{ Clicked row._id }}` for the **Update State** action. Replace this with `{{ Clicked row.emp_no }}` as we want to use the actual primary key and not the URL-safe encoded link.
-
-
-
-### Adding the chart
-
-Under the *Details form block* add a [Chart block](https://docs.budibase.com/docs/blocks#chart-block). Choose a *Chart Type* of 'Line' and the **salaries** table as the datasource. Set the title to 'Salary History'.
-
-
- Salaries Line Chart block
-
-
-Next `Define filters` to add a filter on the employee number as follows:
-
-
-
-> 📘
->
-> The State ID will only be set when a row is clicked, thus the chart display in the builder may display differently than the previewed/published app.
-
-Finally select the **from\_date** column as the *Label Col.* and the **salary** column as the *Data Col.*. A *Stepline* curve is the ideal way to display this data.
-
-
-
-
-
- Config
-
-
-
- Result
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+See [Working with SQL datasources](doc:sql-workflow) for the shared workflow after the connection is in place.
diff --git a/docs/Data/data-sources/oracle.md b/docs/Data/data-sources/oracle.md
index 850b04f01..d242c09c0 100644
--- a/docs/Data/data-sources/oracle.md
+++ b/docs/Data/data-sources/oracle.md
@@ -1,6 +1,6 @@
---
title: Oracle
-excerpt: Connecting Budibase to an existing Oracle database
+excerpt: Connect an Oracle database as a datasource
deprecated: false
hidden: false
metadata:
@@ -10,308 +10,32 @@ metadata:
next:
description: ''
---
-> 👍 SQL Databases
->
-> Budibase is designed to integrate with any core SQL database.
->
-> Read more about [SQL Datasources](doc:sql-datasource).
+Use Oracle when you want Budibase to connect to an existing Oracle database.
-# Tutorial: Building a Departments Portal
+Budibase supports Oracle 12.1 and later in Thin mode and Oracle 11.2 and later in Thick mode.
-In this tutorial we will be building an app to allow executive staff to view and manage department and employee details.
+## Connect the datasource
-To get started, follow the instructions from the [Database Star sample database](https://github.com/bbrumm/databasestar/tree/main/sample_databases/oracle_hr) repo, running the setup scripts against your Oracle instance.
+1. Open the **Data** section
+2. Add a new source
+3. Choose `Oracle`
+4. Enter the connection details
+5. Fetch the tables you want to use
-You may also find this video tutorial helpful for getting started: [How to Set Up the Oracle HR Schema](https://www.youtube.com/watch?v=x8C8w4JM3AU\&ab_channel=DatabaseStar).
+## Fetch tables
-## Adding the datasource
+Fetch only the tables you need. You can fetch more tables later if the schema changes.
-With your Oracle instance running, and the sample database installed, we can begin pulling that data into Budibase.
+## Define relationships
-First create an app - no sample data needed.
+Add relationships after you fetch the tables.
-
+Use one-to-many relationships for foreign keys and many-to-many relationships when your schema includes a join table.
-Once the new app has been created, click `Add source` and select `Oracle`.
+If Budibase rejects a join table, adjust the source tables and fetch them again.
-
+## Custom queries
-Click `Continue` and then fill out the connection config with the host, service name and user of your Oracle instance.
+Use custom queries for joins, grouped results, or write actions from a form or button.
-
-
-Click `Save and fetch tables` to finish.
-
-
-
-## Defining relationships
-
-With our tables fetched, we can now [define relationships](https://docs.budibase.com/docs/sql-datasource#defining-relationships) within Budibase.
-
-Click the `Define relationship` button below the **Table** section to get started, and setup the following relationships:
-
-* One REGION -> Many COUNTRIES
-* One COUNTRY -> Many LOCATIONS
-* One LOCATION -> Many DEPARTMENTS
-* One DEPARTMENT -> Many EMPLOYEES
-
-
- Example relationship: One Country -> Many Locations
-
-
-Once finished you should see a list of your defined relationships:
-
-
-
-It is also worth changing the [display column](https://docs.budibase.com/docs/budibasedb#selecting-the-display-column) on some of the tables so that the links are more human readable. For example edit the **REGION\_NAME** column of the **REGIONS** table and toggle on `Use as table display column`.
-
-
- Setting the region name as the display column
-
-
-
- Linked region in the countries table is now human readable
-
-
-
-
-## Region map screen
-
-Our **REGION** table includes four regions. To start of with, let's present the user with a world map from which they can select a region and further drill down into country > location > department > employees.
-
-Click on the **Design** tab along the top. Next add the [Background image](https://docs.budibase.com/docs/image#background-image) component, with a URL to a [World Map image](https://www.pixelstalk.net/wp-content/uploads/2016/06/Free-desktop-world-map-wallpapers-HD.jpg). Set the *Height* and *Width* to `100%`.
-
-Next add a [Section](doc:using-sections) and set the type to `Three columns` and [Custom CSS](doc:custom-css) to set the height:
-
-```css
-height: 100%;
-```
-
-Next add three [Containers](doc:container). Within each container add a [Headline](https://docs.budibase.com/docs/displaying-text#headline) for each of the Regions: *Americas*, *Europe*, *Middle East and Africa*, and *Asia*. Add margin and adjust the container layouts as desired.
-
-
-
-
-
-## Country screen
-
-With our region map setup, we can add a country screen that will list the countries of the selected region.
-
-Click `Add screen` and select `List view`.
-
-
- Adding an auto-generated screen
-
-
-Click `Continue`, select the **COUNTRIES** table, and then `Confirm`.
-
-
-
-You will now see your new screen with a pre-populated countries [Table block](doc:table-block). Click `Configure columns` in the Settings Panel, `Add all columns`, and then click the `X` to remove the ID columns and the *REGIONS*.
-
-
- Configuring table columns
-
-
-You can see the locations are showing an ID field value, which isn't ideal. We can improve this by changing the [display column](https://docs.budibase.com/docs/budibasedb#selecting-the-display-column). Navigate to the **LOCATIONS** in the **Data** section and set the *City* column to the display column.
-
-We can further improve the user experience by adding country flag images to each row, and allow users to search the columns.
-
-Untick the setting `Show button above table`. All the countries have already been added, and we don't want the user to be able to enter new records.
-
-### Search fields
-
-Adding searchable fields is straightforward. Within the settings panel, select the *COUNTRY\_NAME* and *LOCATIONS.CITY* options under the **Search Fields** property.
-
-
- Selecting search fields
-
-
-The search fields will allow users to performa a "Starts with" search to find countries by name or city.
-
-### Row flag icons
-
-As a nice visual cue, we can also add [Image](doc:image) components to each row of our table. To do this we must first [Eject](https://docs.budibase.com/docs/blocks#ejecting-blocks) the table block. Next select the [Table](doc:table) component, and click the `Add component` button.
-
-Select the image component, and set the *Width* and *Height* to `64px` and `36px`.
-
-Finally, for the *URL* property, click the lightning icon to provide the following JavaScript Binding:
-
-```javascript
-const country = $("Table.COUNTRIES.COUNTRY_NAME")?.toLowerCase()?.replaceAll(" ", "-");
-
-if (country === "united-states-of-america") {
- return "https://www.rankflags.com/wp-content/uploads/2015/04/National-Flag-Of-United-States.jpg"
-}
-
-if (country === "israel") {
- return "https://upload.wikimedia.org/wikipedia/commons/d/d4/Flag_of_Israel.svg"
-}
-
-return `https://images.mapsofworld.com/${country}/${country}-flag.gif`
-```
-
-
-
-### Filter out countries with no locations
-
-We don't want to display countries if they have no departments. Click on the [Data provider](doc:data-provider) and `Define filters`. Add another filter to check if the locations relationship if empty.
-
-
- Don't retrieve locations with no city
-
-
-
-
-## Linking the region to the country screen
-
-While the countries screen is setup, we still need to filter the countries by region and link the screen to the chosen region.
-
-First click on the *Screens* icon button on the left hand panel. In the *Settings Panel* on the right, edit the route to include a [URL variable](doc:url-parameters) for the **region\_name**.
-
-
- Adding a region name to the countries screen
-
-
-Next in the components view, click on the [Data provider](doc:data-provider) and `Define filters`. Click `Add filter`, selecting *REGION\_ID* equals binding: `{{ URL.region_name }}`.
-
-
- Adding a filter on the Region ID
-
-
-Next navigate to the `/home` screen. For each of the headline containers, click `Define actions` for the *On Click* property.
-
-Add the **Navigate To** action, and select the countries screen. Then replace `:region_id` with the particular ID (1-4) for the selected region.
-
-
- Navigate to the country screen for Region 2 (Americas)
-
-
-### Adding the region name to the country screen
-
-Above the 'COUNTRIES' heading, add a [Repeater block](https://docs.budibase.com/docs/blocks#repeater-block) component, and nest the headline under it. Select the **REGIONS** table as the data source, and `Define filters`. Match the region ID against the ID passed in the URL. We can use the *default* handlebars helper to provide a title while in the builder.
-
-
- Filter on the Region ID from the URL, or default to 1
-
-
-For the heading value, select the *REGION\_NAME* binding from the dropdown:
-
-
-
-
-
-## Displaying a list of departments
-
-The next level of data to drill into is the list of departments for the selected country.
-
-In the `/countries/:region_id` screen, delete the **Details form block**. The countries should be static and not editable by the user - we only want to display the list of departments here.
-
-Now in the empty **Details side panel**, add a Repeater block, and nest a Headline component. The Repeater block should have **COUNTRIES** set as the data source, with a filter on the **\_id** column against the *State ID*. This state binding represents the ID of the clicked row.
-
-
-
-> 🚧 Filtering on '\_id'
->
-> The **\_id** field represents a URL-safe encoded id string.\
-> As relationship links in Budibase are stored in this format, this field must be used when filtering on side panel state IDs.
-
-The headline value should bind to the *COUNTRY\_NAME* of the repeater block:
-
-
- Selecting the country name from the repeater block
-
-
-Below the country name headline, add a [Divider](doc:divider) as well as another Repeater block. This will be for the **LOCATIONS** pulled from the relationship of the **COUNTRIES** datasource.
-
-
- Selecting locations from the countries relationship
-
-
-Now within the Locations Repeater block, nest a [Cards block](https://docs.budibase.com/docs/blocks#cards-block) for the **DEPARTMENTS** relationship:
-
-
- Selecting departments from the locations relationship
-
-
-The *Title* of the Cards block should bind to the \_City\_of the location, which can be selected from the drop-down.
-
-Scroll further down the right hand settings panel to get to the *Cards* section. Remove the *Subtitle* and *Description*, but add the following binding for the individual card *Title*: `{{ Departments Cards block.DEPARTMENTS.DEPARTMENT_NAME }}`. This can be selected from the dropdown.
-
-
- Final Details side panel component tree
-
-
-### Hiding locations that have no departments
-
-Currently if a location has no departments, we will see the *No rows found* message.
-
-
- No rows found because Stretford has no departments
-
-
-Ideally we should not display Stretford at all in this case. Click on the *Departments Cards block* and click on `Configure conditions`. Add a [Condition](doc:conditions) to hide the component if **LOCATIONS.DEPARTMENTS** is empty.
-
-
-
-#### Result
-
-
-
-
-
-## Employees screen
-
-The last screen we want is for displaying all the employees of a selected department. Click `Add screen` and add a `List view` screen for the **EMPLOYEES** table.
-
-Change the *Route* to `/employees/:department_id`. Then in the components view, click on the Table block and `Define filters`. Add a filter to match `{{ URL.department_id }}` against `DEPARTMENT.DEPARTMENT_ID`.
-
-You can also eject the table block so that you can deselect some of the employee form fields, such as the ID and the department fields.
-
-Back to the `/countries` screen, click on the *Departments Cards block*. Find the setting called `Link card title` and tick the checkbox. Under *Link screen* select the `/employees/:department_id` screen. There is no need to replace the **:department\_id** in this instance, as Budibase knows to replace this with the **\_id** of the repeater.
-
-
- Linking the card title to the employees screen
-
-
-
-
-## Removing navigation links
-
-As both the employees screen and the countries screen require an ID in the URL, we do not want to show the nav link for these screens.
-
-Click the [Configure links](https://docs.budibase.com/docs/navigation#configure-links) button and click the `X` to remove the link for employees and countries, and click `Save`.
-
-
- Only display the home screen link
-
-
-
-
-## Managing departments
-
-Currently the departments listed for a location are read-only. It may be the case that you want *Admin* users to be able to add new or update existing departments.
-
-Create a new auto-generated List view screen for the **DEPARTMENTS** table, but in this case select *Admin* access level.
-
-
- Only Admin users can view this autogenerated screen
-
-
-You will also want to make sure that the navigation link for the department screens only displays for admin users.
-
-Click on the [Navigation](doc:navigation) icon on the left hand pane, and `Configure links`. Beside the `/departments` link, click the dropdown and select *Admin*, then click `Save`.
-
-
- The departments link will be visible to admin users only - default is basic
-
-
-To verify the permissions, click `Preview` in the upper right hand corner of the **Design** view. You can toggle the user permissions in the blue bar along the top to test.
-
-
- Verifying that admin users can see the Departments link
-
-
-
- Verifying that power users (and below) cannot view Departments
-
+See [Working with SQL datasources](doc:sql-workflow) for the shared workflow after the connection is in place.
diff --git a/docs/Data/data-sources/postgresql.md b/docs/Data/data-sources/postgresql.md
index 301fab185..2ec7dca12 100644
--- a/docs/Data/data-sources/postgresql.md
+++ b/docs/Data/data-sources/postgresql.md
@@ -1,6 +1,6 @@
---
title: PostgreSQL
-excerpt: Using a Postgres Database Server as a datasource for Budibase
+excerpt: Connect PostgreSQL as a datasource
deprecated: false
hidden: false
metadata:
@@ -10,403 +10,34 @@ metadata:
next:
description: ''
---
-> 👍 SQL Databases
->
-> Budibase is designed to integrate with any core SQL database.
->
-> Read more about [SQL Datasources](doc:sql-datasource).
+Use PostgreSQL when you want Budibase to connect to an existing PostgreSQL database.
-# Video tutorial
+## Before you start
-
+Make sure you have:
-
+* A running PostgreSQL instance
+* Credentials for the database
+* Permission to add datasources in Budibase
-***
+## Connect the datasource
-# Tutorial: Inventory management
+1. Open the **Data** section
+2. Add a new source
+3. Choose `PostgreSQL`
+4. Enter the connection details
+5. Fetch the tables you want to use
-In this tutorial we will look at using the [DVD Sample Database](https://www.postgresqltutorial.com/postgresql-getting-started/postgresql-sample-database/) within Budibase. A link to **pgadmin** import instructions are included in that article, but it is worth noting that if you are not on Windows, you will want to extract the zip folder, and restore from *Directory* instead.
+If Budibase is running in Docker and the database is on the same machine, use the host value required by your environment instead of `localhost`.
-
+## Fetch tables and define relationships
-This sample database includes a complete system of film details through to payments and customer details. To keep the tutorial to a modest size, we shall focus on the film details and inventory management.
+Fetch only the tables you need, then add relationships for the joins you want to reuse in screens and bindings.
-
+Use one-to-many relationships for foreign keys and many-to-many relationships when your schema includes a join table.
-## Datasource setup
+## Custom queries
-Assuming you have setup the example database on a running Postgres instance, we can start pulling data into Budibase.
+Use custom queries for joins, grouped results, or write actions from a form or button.
-First create an app - no sample data needed.
-
-
-
-Click `Add source`, select `PostgreSQL` and press `Continue`.
-
-
-
-You will then be presented with a connection configuration modal. Fill in the correct connection details but press **Skip table fetch**. We want to manually fetch tables later so we can choose the tables we want to use in our Budibase app.
-
-> 📘
->
-> Note that if you are connecting to a database on `localhost`, your host should be set to `host.docker.internal`, or `172.17.0.1` if running on Linux.
-
-
-
-### Database Privileges
-
-When connecting to PostgreSQL, the user provided in the connection configuration must have sufficient privileges to read the database schema.
-
-Specifically, the user needs:
-* **USAGE** privilege on the schema (e.g., `public`).
-* **SELECT** privilege on the tables you wish to import.
-
-If the user has `USAGE` privilege but is missing `SELECT` privilege, Budibase may be able to see that the tables exist but will be unable to read their columns. In this case, you will see an error message indicating that the user is likely missing `SELECT` privileges. To resolve this, grant select access to the user in your database:
-
-sql
-GRANT SELECT ON ALL TABLES IN SCHEMA public TO your_user;
-
-
-Now we have the Postgres Datasource added, and the configuration page should be displayed.
-
-Under the **Tables** section press `Fetch tables`. Tick `Fetch listed tables only` and provide the following table names:
-
-```
-category
-film_category
-film
-language
-film_actor
-inventory
-actor
-store
-address
-city
-country
-```
-
-
- Fetch the checked tables from the list
-
-
-
-
-## Defining relationships
-
-With our tables fetched, we can now define relationships within Budibase.
-
-We will need to setup the relationships as presented in the [DVD Rental ER Model](https://www.postgresqltutorial.com/postgresql-getting-started/postgresql-sample-database/), excluding the tables we did not fetch.
-
-Click the `Define relationship` button below the **Table** section.
-
-
-
-
-
-Setup a *Many->Many* relationship between the **category** and **film** tables. Notice the error received below:
-
-
- film\_category is not a valid Budibase join table
-
-
-This error is caused by the *Not NULL* constraint on the **last\_update** column. This is easily resolved by setting this column to *nullable* in **pgadmin**.
-
-
- Setting last\_update to nullable
-
-
-**Do the same for the*last\_update* column in the *film\_actor* table.**
-
-In addition, the **inventory** table is a through table that will need adjusted like so:
-
-
-
-> 📘
->
-> Make sure to re-fetch the tables to pull down the new schema information!
-
-You should now be able to create this *Many->Many* relationship.
-
-
- Many to Many relationship between category and film
-
-
-When all relationships have been defined, you should see something like so:
-
-
-
-
-
-## Stores screen
-
-Add an [autogenerated screen](https://docs.budibase.com/docs/screens#autogenerated-screen) for the **store** table. Change the *Title* to 'Stores'.
-
-Next `Configure columns`, click `Add all columns`, and then remove columns with the `x` so that only the address is displayed.
-
-### Adding a flag icon
-
-We will also add a flag image within the table to indicate the country of the store. It would be possible to write insert statements to add image URLs for the two stores, but for the sake of demonstration we will use the country name from the **store address** relationship.
-
-[Eject](https://docs.budibase.com/docs/blocks#ejecting-blocks) the table block. As there are only two stores, uncheck the pagination of the Data provider.
-
-#### Naive approach: using multiple repeaters
-
-Select the [Table](doc:table) and click `Add component` to nest a [Repeater block](https://docs.budibase.com/docs/blocks#repeater-block). Select the address relationship as the datasource.
-
-
- Using the address relationship as a datasource
-
-
-We then have to nest another repeater block under the first one to hop from the **city** table to the **country** table!
-
-Select the city relationship as the datasource.
-
-
- City relationship of the address table
-
-
-Finally we can nest an [Image](doc:image) component within the City repeater block. Provide the following [JavaScript](doc:javascript) binding:
-
-```javascript
-const countryName = $("City Repeater block.city.country")[0].primaryDisplay.toLowerCase();
-
-return `https://images.mapsofworld.com/${countryName}/${countryName}-flag.gif`;
-```
-
-The *primaryDisplay* property works here because the country name is the [table display column](https://docs.budibase.com/docs/budibasedb#selecting-the-display-column).
-
-
-
-#### Better approach: using a custom query
-
-Adding two nested repeater blocks is not ideal for performance, and is also not very maintainable. Considering that we are looking to get specific data from the relationships, using [custom SQL queries](https://docs.budibase.com/docs/sql-datasource#add-query) is better for this scenario.
-
-In the **Data** section add a query to your PostgreSQL datasource. Give it the name 'Get Country Names', give it a Binding or the **address\_id** with the query:
-
-```sql
-SELECT LOWER(country) as country_name FROM address a
-JOIN city on a.city_id = city.city_id
-JOIN country c ON c.country_id = city.country_id
-WHERE a.address_id = {{ address_id }}
-```
-
-
-
-Make sure to click `Run Query` followed by `Save Query`!
-
-Now back to the **Design** section, the two existing repeater blocks can be replaced with a single new repeater block, using our new query as the datasource. Click the cog icon to open the binding drawer. Assign the **address\_id**.
-
-
- Image repeater block
-
-
-
-
-Finally the image URL can be set to the following Handlebars expression:
-
-```
-https://images.mapsofworld.com/{{ Image Repeater block.Get Country Names.country_name }}/{{ Image Repeater block.Get Country Names.country_name }}-flag.gif
-```
-
-
- Custom query reduces the number of repeater blocks needed
-
-
-### Adding a store map location
-
-First we need to make a [REST API](doc:rest) call to get the longitude/latitude from our address. Go to the **Data** section, and add a REST datasource.
-
-I used the [OpenCage API](https://opencagedata.com/api), which you can sign up to and receive an API key to use.
-
-Select the `GET` method and paste in the URL:
-
-```
-https://api.opencagedata.com/geocode/v1/json
-```
-
-Add a binding called 'location' and give it a default value. Next add two params. One for your API **key**, and another for the **q** (query string). Click the lightning bolt icon to select the *location* binding.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-To make sure we're pulling the co-ordinates of the first result, add the following to your [Transformer](doc:transformers):
-
-```javascript Transformer
-return data.results[0]?.geometry
-```
-
-After clicking the `Send` button, your Schema should include the latitude and longitude. Make sure to also click the `Saved` button!
-
-
-
-We will need to pull the **city** name and **country** name for a given **address\_id** in order to make use of this API. As before, a custom SQL query is the most efficient way to do this. In fact we can update our existing *Get Country Name* query without breaking the current usage for the flag image.
-
-Click on the *Get Country Names* query under the PostgreSQL datasource. Update the query to pull back the city name as follows:
-
-```sql
-SELECT LOWER(country) as country_name, city FROM address a
-JOIN city on a.city_id = city.city_id
-JOIN country c ON c.country_id = city.country_id
-WHERE a.address_id = {{ address_id }}
-```
-
-The schema should now have two fields: one for the country, and one for the city. Make sure to run and save the query.
-
-
-
-With the REST query set up, go back to the **Design** section. Within the *Details side panel*, eject the *Details form block*. This will give us access to the **store** entry data. Click on the Repeater and add a repeater block with the name 'Location Repeater Block', which will be nested below. Set the *Get Country Names* as the datasource, and click the cog icon to pass in the binding `{{ Repeater.store.address_id }}`. Drag the Form into the new *Location Repeater Block*.
-
-Currently the heading is displaying the ID value of the store. This isn't very nice, so let's replace it with the store location. Replace the [Heading](https://docs.budibase.com/docs/displaying-text#headline) *Text* handlebars with the following:
-
-```
-{{ Location Repeater block.Get Country Names.city }}
-```
-
-You can use the bindings helper on the left hand panel to insert this value. It is also unlikely that you would want *\_id* fields in the form, so go ahead and delete those.
-
-Next nest a [Data provider](doc:data-provider) under the Location repeater block, below the form. Set the datasource to REST query we added earlier, and click the cog icon to pass in the following binding for the query string param:
-
-```
-{{ Location Repeater block.Get Country Names.city }}, {{ Location Repeater block.Get Country Names.country_name }}
-```
-
-Finally nest an [Embedded Map](doc:map) underneath the REST query data provider. Choose it as the *Provider* and select the *lat* and *lng* keys.
-
-
-
-
-
-## Viewing available films in store
-
-Create a new screen with the route: `/film/:storeid`.
-
-Add a [Row Explorer block](https://docs.budibase.com/docs/blocks#row-explorer-block) for the **film** table. Set the *Height* to `800px` and set the *Search Field* to `title`. Assign the *Title*, *Subtitle*, and *Description* to some appropriate bindings, e.g.
-
-* `{{ Film Explorer Block.film.title }}`
-* `{{ Film Explorer Block.film.release_year }}`
-* `{{ Film Explorer Block.film.description }}`
-
-
- Selecting the film description from the binding dropdown
-
-
-As we want to filter the list of films by the *store\_id* that we [pass via the URL](doc:url-parameters) go ahead and eject the Row Explorer block, and `Define filters` for the now exposed Data provider.
-
-Click `Add filter` to add an additional filter for the *store\_id* column.
-
-
-
-Back to the `/store` screen, click on the table and add a new [Link](doc:link) component. Give it the name 'View films' and the URL binding: `/film/{{ Table.store.store_id }}`.
-
-
-
-### Adding the film rating image
-
-At the moment the row explorer presents the film information in a basic form. This is fine, but let's make it look a little nicer.
-
-First remove the **rating** field, and any of the fields that you do not wish to be displayed or edited from the [Form block](doc:form-block). Next eject the form block so that the data provider can be used.
-
-
- Selecting the form block fields
-
-
-Above the recently exposed form, add an image component. Give it the following JavaScript *URL* binding:
-
-```javascript JavaScript
-let baseUrl = "https://www.motionpictures.org/wp-content/uploads/2018/03";
-const rating = $("Repeater.film.rating");
-
-if (rating === 'G') {
- return `${baseUrl}/general_audiences_badge.svg`;
-}
-else if (rating === 'PG') {
- return `${baseUrl}/parental_guidance_badge.svg`
-}
-else if (rating === 'PG-13') {
- return `${baseUrl}/parents_strongly_cautioned_badge.svg`
-
-}
-else if (rating === 'R') {
- return `${baseUrl}/restricted_badge.svg`
-}
-
-return `${baseUrl}/no_17_and_under_badge.svg`
-```
-
-As these images are white, you'll also want to edit the [Custom CSS](doc:custom-css) and give it a black background: `background-color: black;`. Also set a *width* and *height*, e.g. 256px \* 96px.
-
-
-
-
-
-### Displaying inventory count
-
-Within the film view, it would also be useful to show the user what the current stock is for the selected film.
-
-In the **Data** section add another custom SQL query.
-
-```sql
-select COUNT(*) from inventory
-WHERE film_id = {{ film_id }} and store_id = {{ store_id }}
-GROUP BY film_id
-```
-
-
-
-As always, be sure to `Run Query` and `Save Query`.
-
-Back in the **Design** section, add a new Repeater block above the row explorer detail heading. Select the inventory count query as the datasource. Click the cog icon to pass in the binding values:
-
-
-
-Nest the Heading component under the Stock Repeater block and give it the binding *Text* value: `Stock: {{ Stock Repeater block.Get Inventory Count.count }}`.
-
-
-
-***
-
-# Using query bindings in more complex queries
-
-For the most part Bindings in PostgreSQL queries will behave much in the same way as other [Custom queries](https://docs.budibase.com/docs/data-sources#custom-queries) and as shown in the tutorial above.
-
-It is worth understanding however, that SQL queries will be [parameterized](https://www.postgresql.org/docs/current/sql-prepare.html) under the hood. This is mainly done to improve security and performance.
-
-This enhanced security does have a small drawback in reducing the flexibility of your queries. For example, bindings cannot be used as a direct inline replacement of text in your queries, and therefore cannot be used to dynamically set table or column names, dynamically set operators, or inject SQL snippets. Ideally your Budibase bindings should be used in the same way that you might use **$** parameters in your Postgres queries.
-
-## Example: Using a binding with INTERVAL
-
-Let's say you have the following query:
-
-```sql
-select * from public."ServiceLog" where "ServiceDate" > NOW() - INTERVAL '2 years'
-```
-
-And you want to add bindings to make the `INTERVAL` adjustable. This can be done like so:
-
-
- Query an interval of 'n' years
-
-
-Alternatively, if we wanted to provide an INTERVAL string, then we need to use Postgres typecasting in our query like so:
-
-
- Text binding input must be cast to Postgres interval
-
+See [Working with SQL datasources](doc:sql-workflow) for the shared workflow after the connection is in place.
diff --git a/docs/Data/data-sources/redis.md b/docs/Data/data-sources/redis.md
index 222e16df2..5d75b0d01 100644
--- a/docs/Data/data-sources/redis.md
+++ b/docs/Data/data-sources/redis.md
@@ -1,65 +1,52 @@
---
title: Redis
-excerpt: ''
+excerpt: Connect Redis as a datasource
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5hgjm
+ description: ''
robots: index
next:
description: ''
---
-Redis is an open source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine.
-
-More information can be found in the [official Redis docs](https://redis.io/docs/about/).
-
-## Connect
-
-Add a data source and select Redis.
-
-
-
-
+Use Redis when you need a fast key-value store for simple read and write operations.
-You will then be prompted to provide the URL, port, username and password. The default database index is 0.
+For the official Redis documentation, see the [Redis docs](https://redis.io/docs/about/).
-
+## Connect Redis
-> 📘 Connecting to a local docker instance
->
-> If you are hosting Redis locally with docker, then use **host.docker.internal**
+1. Add a datasource and select `Redis`
+2. Enter the connection details
+3. Save the datasource
-## Redis Command
+If you are using Redis locally in Docker, use `host.docker.internal` as the host value.
-Select `Redis Command` from the *Function* dropdown. You will now be able to enter any valid Redis command into the query box below.
+## Use the Redis Command query
-
+`Redis Command` is the most flexible query type. Use it when you want to run a Redis command directly.
-This is the recommended function as it is the most flexible. The basic commands can be found below.
+1. Open the Redis datasource
+2. Create a query with the `Redis Command` function
+3. Enter a valid Redis command in the query box
+4. Test and save the query
-For more information on the range of Redis data types and commands, click [here](https://redis.io/docs/data-types/).
+## Common operations
### Create
-This is the equivalent of Redis' SET command. Enter a **key / string value** pair.
-
-**Ttl** stands for *Time-to-live* and is the number of seconds that the data structure will exist. Leave this blank if you want your key/value to never expire.
-
-
-
-Only **strings** are supported by this function.
+Use `SET` to create or update a key. You can also set a TTL in seconds.
### Read
-This is the equivalent of Redis' GET command. Simply enter a **key** you want to get. The response will return the mapped value.
-
-
-
-
+Use `GET` to return the stored value for a key.
### Delete
-This is the equivalent of Redis' DEL command, except you can only delete a single **key** at a time.
+Use `DEL` to remove a key.
+
+## Notes
-
\ No newline at end of file
+* The basic query examples only support string values
+* Use the command query when you need more than the built-in helpers
+* Refer to the Redis docs for supported data types and commands
diff --git a/docs/Data/data/index.md b/docs/Data/data/index.md
index d18731a56..f34b54b5b 100644
--- a/docs/Data/data/index.md
+++ b/docs/Data/data/index.md
@@ -25,38 +25,53 @@ next:
slug: rest
title: REST API
---
-The data section in the Budibase UI is where you add and manage your data. Budibase workspaces rely on data and it is strongly advised to create your data structure before designing your web application or automation. Your data structure will involve tables, columns, rows, and views - these are the building blocks that hold and present your data in a structured manner.
+Use the Data section to manage the tables, views, and datasources that power your workspace.
-Predefining the data structure also greatly helps in building the UI as the UI can be automatically generated based on the data structure, as well as any logic, filtering, and sorting.
+Your data model usually comes first. It shapes the screens, automations, filters, and permissions you build later.
-Within Budibase we have a single built-in Datasource and several external datasources. When using a self-hosted instance of Budibase, an external datasource can still live on the same device, just not within the Budibase container.
+## What data is for
-Any datasources you create will be available to use within all apps and automations in your workspace.
+Use data to:
-## Getting started with data
+* Store rows and fields in a structured way
+* Define relationships between records
+* Control what users can read or write
+* Generate screens and automation inputs
-By default, every Budibase workspace will start with a Budibase DB datasource. This database contains a [Users table](doc:users-table) which is considered a special table that cannot be removed. This table includes all the users who have access to your application, with their respective roles.
+## Budibase DB
-{`
-
-`}
+Every workspace starts with a Budibase DB datasource.
-## Adding new datasources
+The built-in `Users` table lives there too and cannot be removed.
-There are a broad set of available external datasources you can connect to right from the Budibase UI.
+Use Budibase DB when you want the simplest path to app data or when you are prototyping a workflow.
-You can add new data sources by clicking the `+` button on the left-hand panel when you're on the Data tab, as seen in the image below.
+## Add datasources
-
- Adding a datasource
-
+Add datasources from the Data tab.
-Once you've clicked the `Add source` button, you'll see a modal presented with the available datasources. Pick your datasource of choice, and you will be presented with a configuration screen to connect to your remote datasource. You can also [use AI to generate](/docs/ai-powered-table-generation) a new Budibase DB table for you.
+Common source types include:
-
- Choose a datasource
-
+* External SQL databases
+* REST APIs
+* Document databases
+* Budibase DB tables
-You can add as many datasources as you need following the same process.
+After you add a datasource, you can create tables, configure views, and reuse that data across apps and automations in the same workspace.
-To know more about a particular datasource, look at the relevant documentation page in the menu.
+## Data workflow
+
+A typical workflow looks like this:
+
+1. Define the data model
+2. Add the datasource
+3. Configure tables and columns
+4. Add views and permissions
+5. Build screens and automations on top of that data
+
+## Related guides
+
+* [BudibaseDB](doc:budibasedb)
+* [PostgreSQL + MySQL](doc:postgresql)
+* [MongoDB](doc:mongodb)
+* [REST API](doc:rest)
diff --git a/docs/Data/data/users-table.md b/docs/Data/data/users-table.md
index 05b9eb6a4..474f58bdd 100644
--- a/docs/Data/data/users-table.md
+++ b/docs/Data/data/users-table.md
@@ -10,128 +10,43 @@ metadata:
next:
description: ''
---
-The **App users** table is a special Budibase DB table that stores all of the users that have access to the app.
+The **App users** table stores the users who can access your app.
-It is anchored to the top of the Datasource list, and has a unique icon to differentiate it from other tables:
+It is a read-only Budibase DB table that sits at the top of the datasource list.
-
+## What it contains
-Unlike regular tables, you cannot add rows to the users table, this is a read only table. In addition, rows cannot be deleted so that the audit trail remains intact even if a users' access is revoked.
+The users table includes standard user fields and any custom columns added by Budibase.
-[User Management](doc:user-management) is handled through the [Admin Portal](doc:portal), and the [Users panel](https://docs.budibase.com/docs/application-access).
-
-The standard columns include:
-
-
+You can use those values in bindings, filters, and conditional logic.
## Updated By columns
-It is possible to add auto-columns to your tables, which includes the *updated by* column.
-
-
- Adding a 'Updated By' auto-column
-
-
-This will create a linked column in the Users table with the following name format:
-
-* \[table name]-updated\_by
-
-These can be used to display user specific data for the currently logged-in user.
-
-When a row is created or updated through the UI, the user will appear in the 'Updated By' column.
-
-
-
-> 🚧 Autogenerated column
->
-> The wand icon indicates that this column is autogenerated and auto-populated.\
-> The values cannot be overwritten or saved manually.
-
-## Edit roles
+When you add an `Updated By` auto column to another table, Budibase creates a linked field in the users table.
-You can also add custom roles to your app. More information [here](https://docs.budibase.com/docs/user-roles#adding-custom-roles).
+Use these columns to display user-specific data for the current logged-in user.
## Current User bindings
-There are globally available Bindings for every column in the Users table, including the custom columns.
-
-
-
-For example, these can be used to display the name of the currently logged-in user, or [Add filtering](doc:searchfilter-data) on the **\_id** to get related user data.
-
-### OAuthToken
-
-When the user logs in using SSO, an [OAuth token](https://docs.budibase.com/docs/authentication-and-sso#using-the-oauth-token) will be available for use in API headers.
-
-***
-
-## Tutorial: Create/Edit user details screen
-
-You may want to allow a method for users to add additional details. This tutorial will outline how to create a table for users to add additional details about themselves. This requires creating an additional table called user details.
-
-#### Step 1 - Create a new table
-
-Add a new internal table data source in the data section and create a new table called user details. You can add as many fields as you like here, such as avatar, number, etc.
-
-#### Step 2 - Add a single user column
-
-Add a single user column to this table. Later, this will be used to assign a row's ownership to a specific user. As well as do filtering for conditional logic applied to components.
-
-#### Step 3 - Add the edit user profile button
-
-Navigate to **Design**, and click `Add component` and select [Sections](doc:using-sections) leaving the *Type* as `Main with sidebar`.
-
-Next add a [Headline](https://docs.budibase.com/docs/displaying-text#headline) with the following text Binding: `Welcome back {{ Current User.firstName }}!`.
-
-Now you could easily add a [Button](doc:button), but it would be nice to include a user icon for editing profile info, therefore add a [Container](doc:container) instead and set a background and border.
-
-Inside the container, add an [Icon](doc:icon), selecting *user-fill* and set the *Size* and *Color*. Then add a [Paragraph](https://docs.budibase.com/docs/displaying-text) component with the text 'Create my profile'.
-
-
-
-#### Step 4 - Add a Data provider
-
-Add a [Data provider](doc:data-provider), limit this to 1, and select your user details table as the source. Then apply the following filter;
-
-
-
-This will only pull rows that are assigned to the user via the user column we made earlier. We will use this later to do some conditional rendering on which form will be used depending on whether the user has already created a record.
-
-#### Step 5 - Create/Edit my profile side panel
-
-Add a [Side panel](doc:side-panel), and two nested [Form blocks](doc:form-block). Select the 'user details' *Table*. Set the second form block *Type* to 'Update'.
-
-In the second form block, set the *Row ID* to: `{{ User details data.Rows.0._id }}` and provide a *Title*.
-
-
-
-Select the **Container** and click `Define actions` beside the *On click* setting. Add the **Open Side Panel** and select the side panel in the dropdown.
-
-
-
-Add default values to the user field inside each of the form blocks. You can do this by clicking on the cog icon next to the field in the form block's settings. Once the context menu is open, click the lightning bolt next to the "default value" field and then apply this binding to the field `{{ Current User._id }}`.
-
-#### Step 6 - Finishing off
-
-Add conditions to the two forms. For this, we will be using the [Data provider](doc:data-provider) we discussed previously. For the create form, we will be using the below condition;
+Use the `Current User` bindings to access logged-in user data in the builder.
-
+Common uses include:
-Both forms will use this initial binding `{{ User details data.Rows Length }}`. For the update form, we will be using the below condition;
+* Displaying the current user name
+* Filtering rows assigned to the current user
+* Using the user OAuth token in API headers
-
+## User columns
-Lastly, we will add an if statement to the paragraph to communicate to the user whether they are editing or creating a record. The code below is how we will do this, once again we will be using the [Data provider](doc:data-provider) for this logic.
+Budibase now prefers user columns over direct relationships to the users table.
-```Text JS
-if($("User details data.Rows Length") > 0) {
- return "Edit my profile"
-}
-return "Create my profile"
-```
+If you have an older app, you can migrate existing user-table relationship fields to a user column.
-## User column
+## Migrate existing relationships
-With the implementation of the new `User column` eventually custom fields to the user's table will become depreciated. Meaning that you will no longer be able to add custom fields to the user's table.
+1. Open the table that contains the user relationship
+2. Open the table menu
+3. Choose `Migrate to user column`
+4. Follow the prompts
-The new intended approach will be to create a new table e.g. `Users Metadata` and include all the additional user related information here. Then link the users to their rows within this newly created table. From here you will then be able to easily access additional fields based on the user link created with the User column.
\ No newline at end of file
+Update any bindings that referenced the old relationship field after the migration.
diff --git a/docs/Data/data/views.md b/docs/Data/data/views.md
index 2ec3c07bb..438b18230 100644
--- a/docs/Data/data/views.md
+++ b/docs/Data/data/views.md
@@ -1,6 +1,6 @@
---
title: Views
-excerpt: Learn more about views within Budibase
+excerpt: Create filtered or grouped views from table data
deprecated: false
hidden: false
metadata:
@@ -10,43 +10,17 @@ metadata:
next:
description: ''
---
-Views are virtual tables that present data from a table with a preset filter or grouping.
+Use views when you want to present table data with a preset filter, sort order, or grouping.
-When you create a View within Budibase, it presents you with all the data from its linked table. This provides you with a canvas to manipulate your data without changing the data within the original table. This is crucial, as it protects your original data. It also allows you to create:
+Views are virtual tables. They do not change the underlying data.
-1. Filters
-2. Sorts (including multi-column sorting)
-3. Hide columns
+## Create a view
-When designing your UI, you can use a View with auto-generated screens, as well as for custom UI development. Say you have a table of Sales employees, and their total sales, you can create a view which gives you the total sales of all Sales employees. You can then reference the data from this View in the *Design* section of Budibase.
-
-## How to create a new view
-
-To create a new view, click the `Create view` button, and give it a name.
-
-{`
-
-`}
-
-## Filters
-
-One of the core features of views is the ability to filter rows according to particular criteria. When a Row is filtered, it is not deleted—it's just hidden from the particular view you're using and remains within the table (views are virtual tables!).
-
-Try adding filters when:
-
-* You need to focus on rows that meet certain conditions (e.g. Classes between May and June, and taught by Albert Einstein).
-* You need to display certain information, to certain people (e.g Each salesperson can only view their own commission).
-
-### How to create a new filter
-
-To create a new filter:
-
-1. Click the `Filter` button
-2. Click `Add Filter`
-3. Select the Column you would like to filter
-4. Select a filter function
-5. Type your query
-6. Click `Saved`
+1. Open the table.
+2. Click `Create view`.
+3. Give the view a name.
+4. Add any filters, sorting, grouping, or hidden columns you need.
+5. Save the view.
You can also save a filtered table directly as a new view.
@@ -114,26 +88,36 @@ We can then *Group by* additional criteria to refine the data further. For examp
Adding sales channel to the grouping breaks the counts down by both country and channel.
-## Views and View Calculations as Data Sources
+Use views when you need to:
-The Screens button in the Data toolbar shows where a view or view calculation is currently being used.
+* Focus on a subset of rows
+* Hide or sort data without changing the source table
+* Sort with one or more columns
+* Reuse the same filtered dataset in screens or repeaters
-Views and View Counts can be used as Data Sources, and manipulated in the same ways. Using a view calculation like "Calculate the Sum of Total Revenue, Group by Country" allows us to find the total revenue of sale per country. This data can then be used in components like **Charts** or **Repeater Blocks**.
+## View calculations
-Add a Repeater Block, and set the Data Source to the `view total_revenue_by_country`, then set the Sort Column to be Sum Total Revenue, descending, limited to 10. This will show us the top 10 countries by sum total sales revenue.
+View calculations let you aggregate data from a table.
-Inside the repeater block, add a container and nest inside it 3 Headline components
+Supported calculations include:
-The repeater example is built from a container with three nested Headline components.
+* Average
+* Sum
+* Minimum
+* Maximum
+* Count
-Set the first headline component to `{{ add New Repeater Block.Row Index 1 }}` - This is taking the Row Index and adding 1 to it. (Index starts counting from Zero, so in order to use row-index as the rankings we must add 1)
+Use grouping when you want one result per category instead of a single total.
-Set the second headline component to `{{ New Repeater Block.total_revete_by_country.Sum Total Revenue }}` - this fetches the sum total of the revenue. You can then set the third headline component to`{{ New Repeater Block.total_revete_by_country.Country }}` , and just like that we've built a basic leaderboard for Total Revenue by Country.
+## Track usage
-### Tracking View Usage
+Use the **Screens** button in the Data toolbar to see where a view or view calculation is used.
-In the Data section, you can use the **Screens** button in the toolbar to see a list of every screen and component where a View or View Calculation is currently being used. This makes it easier to perform impact analysis before modifying or deleting a view.
+This helps you understand the impact before changing or deleting it.
-Note that while standard Views support **Generate app screen** to automatically create new UI based on your data, this feature is not supported for View Calculations.
+## Notes
-The finished leaderboard combines the row index, total revenue, and country into a ranked list.
+* Views can be used as data sources in the Design section
+* View calculations are not supported by Generate app screen
+* If you rename or delete a column used by a view filter, update or remove the filter
+* Budibase fails closed when a filtered column is no longer valid, so the view returns no rows until it is fixed
diff --git a/docs/Data/datasources-copy.md b/docs/Data/datasources-copy.md
index addd35fe7..c70d22318 100644
--- a/docs/Data/datasources-copy.md
+++ b/docs/Data/datasources-copy.md
@@ -12,113 +12,44 @@ next:
---
Budibase can store your data inside the [native internal database](doc:budibasedb) (CouchDB). This is the default datasource unless you opt to connect another datasource to Budibase.
-
- List of app datasources
-
-
Budibase supports connection to a range of remote datasources. More information about connecting Budibase to each of these datasources is nested in this section of the documentation.
Furthermore, if a datasource you want to use is missing, it is possible to build a [custom datasource](https://docs.budibase.com/docs/custom-datasource).
## Schema
-When [Fetching tables](https://docs.budibase.com/docs/sql-datasource#fetching-tables) from external SQL databases, Budibase will attempt to match the external data types to the internal [Budibase DB](https://docs.budibase.com/docs/budibasedb) types. These default types can be changed by clicking the three dots icon beside the table column, and selecting `Edit column`:
-
-
- Edit column
-
-
-
-
-For other basic datasources and [Custom queries](https://docs.budibase.com/docs/data-sources#custom-queries), the schema will be determined by selecting types in the query output.
+When [Fetching tables](https://docs.budibase.com/docs/sql-datasource#fetching-tables) from external SQL databases, Budibase matches the external data types to the internal [Budibase DB](https://docs.budibase.com/docs/budibasedb) types.
-
+For other basic datasources and [Custom queries](https://docs.budibase.com/docs/data-sources#custom-queries), the schema is determined by the types in the query output.
The schema is important, as it will affect how [sorting and filtering](https://docs.budibase.com/docs/data-provider#pagination-and-sorting) is applied in the design section.
## Sample data
-Budibase provides the option of adding some sample data to allow new users to explore the various [Data types](https://docs.budibase.com/docs/budibasedb#data-types) available, and quickly build an app for learning purposes. While it is an internal Budibase DB source, it will be kept separate to the other internal DB tables that you create.
-
-
- Use sample data
-
+Budibase includes sample data so new users can explore the available [data types](https://docs.budibase.com/docs/budibasedb#data-types) and build an app quickly. It stays separate from the internal DB tables you create.
If you do not need this data, it can easily be removed in the same way as other additional datasources. Simply click on the ellipsis to the right of the sample data source and click *Delete*.
-
- Deleting sample data
-
-
## Security
-When connecting to an external datasource you must allow Budibase access. Normally datasources will be protected by a firewall and you will need to open firewall access to your datasource from the Budibase cloud or from your self-hosted Budibase. Port numbers and user management differ across each type of datasource so if you have difficulty connecting you should speak to the Database Administrator for your organisation.
-
-
-
-***
+When connecting to an external datasource you must allow Budibase access. Datasources are usually protected by a firewall, so you may need to whitelist the Budibase cloud or your self-hosted Budibase server.
-
+## Custom queries
-# Custom queries
+Budibase supports custom queries for advanced joins and server-side filtering.
-Budibase supports the ability to write custom queries. These queries can be really useful if you want to perform advanced joins or server-side filtering.
-
-To write custom queries, head over to your Datasource, and scroll down until you find the queries section. Click the `Add Query` button, which will guide you to a form to configure your query.
+To write one, open a datasource and go to the **Queries** section.
> 📘 Budibase DB
>
-> The internal Budibase DB is a unique datasource that does not support custom queries.\
-> It does however support [Views](https://docs.budibase.com/docs/views)
-
-An example would be to query all pet names from a specific owner. For this demonstration we've used a custom binding to insert the `ownerID` into the query.
-
-
-
-> 📘 Read or Write
->
-> If you want to use a custom query with a [Data provider](doc:data-provider), you must select **Read** as the function of your query.
->
-> Other query types, such as Create, can be used by the [Execute Query action](https://docs.budibase.com/docs/data-sources#tutorial-posting-form-data-to-a-custom-query).
-
-## Using query bindings
-
-You can also override the default binding values.
-
-When you use the query as a data source for your provider, you can click on the cog icon.
-
-
-
-This will show a query bindings drawer where you can add a dynamic value to override the default.
-
-
-
-## Editing custom queries
-
-A list of your custom queries can be found underneath the associated data sources. Click on the query to be able to edit the config.
-
-It is also possible to **delete** queries that are no longer needed, or **duplicate** queries, which can useful in cases where an additional query only requires minor changes.
-
-To access these options, hover over the query and click the **…**
-
-
-
-## Tutorial: Posting form data to a custom query
-
-1. Add your query to a datasource, in this example we'll use [PostgreSQL](https://docs.budibase.com/docs/postgresql-1). Add your bindings with some optional defaults, and then reference them within your query.
-
-
-
-2. Add a [Form](doc:forms) with fields for each of the bindings, except for the *id* which will come from the [URL ID](https://docs.budibase.com/docs/url-parameters) binding.
-
-
+> The internal Budibase DB does not support custom queries. It does support [views](https://docs.budibase.com/docs/views).
-3. **Define actions** for the 'Submit' button. Choose the **Execute Query** action and pick *PostgreSQL* as the datasource. You should now be able to see and select your custom query.
+Use `Read` queries with a [Data provider](doc:data-provider).
-
+Use `Execute Query` for create, update, or delete actions.
-4. Finally map your form field bindings to your custom query bindings like so:
+Add bindings when the query needs dynamic values, and use the query settings to override defaults at runtime.
-
+You can edit, duplicate, or delete queries from the datasource list.
-Click **Save** to finish.
\ No newline at end of file
+For the standard SQL workflow, see [Working with SQL datasources](doc:sql-workflow).
diff --git a/docs/Data/environment-variables.md b/docs/Data/environment-variables.md
index 89c502062..75185754a 100644
--- a/docs/Data/environment-variables.md
+++ b/docs/Data/environment-variables.md
@@ -1,83 +1,59 @@
---
title: Environment variables 🔒
-excerpt: Configuration for apps which switches per environment
+excerpt: Configure values that change by environment
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5hgqq
+ description: ''
robots: index
next:
description: ''
---
-Environment variables provide a mechanism for easily re-using secrets/datasource configuration across different Workspaces, as well as allowing for different values to be used in development apps versus production apps. The environment variable configuration can be found under Settings, then Environment.
-
-This feature is most useful when working with data that is already used in production or updating an app that is already being used. For example lets say you have two Postgres databases, one used locally for testing and one used in production for the applications real data. Environment variables can be utilised in this scenario to allow the connection details for the two different databases to be parameterised and changed depending on whether the app is in development, or in production.
-
-## Unlocking environment variables
-
-If you are using the free tier you will see the following under the environment variables section:
-
-
-
-You can click [Upgrade](doc:usage) to unlock environment variables or click `View Plans` to navigate to the [pricing page](https://www.budibase.com/pricing) to see the full range of options in more detail.
-
-## Managing environment variables
-
-Once you have navigated to the environment variable section of the Budibase portal, you will see an option to add a new variable as shown below, but you can also edit and delete your environment variables too.
-
-
-
-Once the modal opens you'll be able to set the environment variable name and its value in production and development. Note that once these values have been set they will be encrypted (uses **AES-256** encryption), stored and you will no longer be able to retrieve it. By default this will use the same value for development as production, incase you have not yet setup two separate datasources, this way you can build your apps utilising environment variables, then later update them if you want to separate the development and production values.
-
-
-
-> 🚧 Environment variable naming
->
-> It is important to pick recognisable names for your environment variables, as these will be how you pick which variables to use within your apps. Your variables also cannot contain spaces, we recommend the use of underscores instead.
-
-At this point you should have created your required environment variables and they should have appeared within the variable table list. From here you can also edit or delete an environment variable, it is important to note the change will be immediate, if you change a value or remove an environment variable, production apps will be affected.
-
-
-
-To edit or delete an existing environment variables, click the `Edit` button.
-
-
+Use environment variables when you need different values in development and production.
-## Using environment variables in datasources
+They are useful for secrets, datasource credentials, and environment-specific endpoints.
-Once you've configured some environment variables we can progress to creating or updating an app to use these new variables. Enter an app and create or update a [datasource](doc:data-sources) from the data section, in this example we will be creating a Postgres connection. Below you can see the default Postgres connection details, you will also notice that the small key icon on the end of the input field.
+## Unlock environment variables
-
+Environment variables are locked on the free tier.
-Clicking on this icon will open the environment variable menu, which allows you to select from a list of variables that have been created. You will also have the option to create a new environment variable.
+Upgrade your plan to use them.
-
+## Create environment variables
-Once configured the input fields will be disabled, with the environment variable contained within, as shown below.
+1. Open **Settings > Environment**
+2. Add a new variable
+3. Set the development value
+4. Set the production value
+5. Save the variable
-
+Variable names cannot contain spaces. Use underscores instead.
-It is important to note that while they appear to look like bindings, they cannot be used as such. The environment variable values are only available in a few key locations and cannot be used in the same way that bindings function. Once this is complete you can save the datasource and it will now utilise the variable.
+Values are encrypted and cannot be retrieved after saving.
-At this point your app is now utilising these environment variables, within the app builder you will be using the development values, once you publish the app it will utilise the production values. It is important to note that these environments are entirely independent, you can be using the builder, with the development values for your datasources while your users are utilising the currently released version of your app and the production values.
+## Use environment variables in datasources
-## Using environment variables in queries
+Use environment variables in datasource settings when the connection details change by environment.
-Another useful feature of environment variables is their ability to be used in [queries](https://docs.budibase.com/docs/data-sources#custom-queries) as well as datasource configuration. If you open the [binding drawer](doc:bindings) you will see a new environment section has been added, as seen below.
+After you select a variable, the field is locked to that value.
-
+## Use environment variables in queries
-Expanding the environment section will list all of the variables which are available to the query, unlike datasource configuration, these can be used as normal bindings in queries. In the example below we have configured a header in [REST query](doc:rest) to use an environment variable.
+Use environment variables in query bindings when the value should be shared across requests.
-
+Example:
-These can also be used in an [SQL query](https://docs.budibase.com/docs/data-sources#custom-queries), in the same format as the binding drawer, like `{{ env.env_variable }}` this is shown below.
+```handlebars
+{{ env.env_variable }}
+```
-
+## Use environment variables in automations
-## Using environment variables in automations
+Use environment variables in automations for reusable values such as webhook URLs or external service credentials.
-The last place that variables can be accessed is as part of an [automation](doc:automation-steps) run. This can be very useful if connecting to an external service, or triggering a notification e.g. Slack or Discord. Like queries environment variables can be used as [bindings](doc:bindings) within automation steps. Below we have configured a Discord step in an automation, which utilises an environment variable for the webhook URL. Not only is this more re-usable and secure, but it also allows using different URLs for testing and for production.
+## Notes
-
\ No newline at end of file
+* Development and production values are separate
+* Publishing pushes the development configuration to production
+* Environment variables are available in datasources, queries, and automations where supported
diff --git a/docs/Data/pdf-exporting.md b/docs/Data/pdf-exporting.md
index 483809175..0d200e102 100644
--- a/docs/Data/pdf-exporting.md
+++ b/docs/Data/pdf-exporting.md
@@ -1,14 +1,11 @@
---
title: PDF exporting 🔒
-excerpt: >-
- The PDF Exporting feature allows you to generate downloadable PDF reports
- directly from your Budibase app similar to how you would build standard
- screens.
+excerpt: Generate downloadable PDF screens from your app
deprecated: false
hidden: false
metadata:
title: ''
- description: kba_5hgsi
+ description: ''
robots: index
next:
description: ''
@@ -17,41 +14,40 @@ next:
slug: screens
title: Screens
---
-> 📘 Licensing🔒
->
-> The following feature is only available to customers with a **Premium** or **Enterprise** plan.
->
-> You can view more details on [our pricing page.](https://budibase.com/pricing/)
-
-## Creating a PDF Screen
-
-When creating a new screen, you’ll see a [screen](doc:screens) option called PDF at the end of the screen list. Select this option to create a screen specifically for generating PDF documents.
-
-
+Use PDF exporting when you want users to download a screen as a PDF.
-Once selected, you’ll be taken to a new screen where you can begin creating your PDF. This includes standard grid functionality, allowing you to arrange content flexibly.
+This feature is available on Premium and Enterprise plans.
-## Building Your PDF
+## Create a PDF screen
-Within the PDF screen, you can use Budibase components such as tables, text, images, and more, just as you would on a regular screen. You can connect [data providers](doc:data-provider) to populate your report with dynamic content using Bindings. This enables you to generate personalised or context-specific PDFs for your users.
+1. Create a new screen
+2. Choose `PDF`
+3. Add the components you want to appear in the document
+4. Bind any data you need
+5. Save the screen
-
+## Build the PDF
-## Styling and Layout
+Use standard Budibase components such as:
-You can fully customise the layout and design of the PDF using Budibase’s grid functionality. [Components](doc:components) will be rendered in the exported file as on the PDF.
+* Tables
+* Text
+* Images
+* Data providers
-
+The PDF screen uses the same grid layout as a regular screen.
-## Customisation options
+## Configure the download experience
-In addition to adding Budibase components to your PDF screens, you can customise the title and button text used for the PDF download experience. These options are available in the screen’s settings panel.
+Use the screen settings to set:
-Both fields support Bindings, allowing you to tailor the PDF to the logged-in user or any other context-specific data. For example, you can include the user's name or role in the title or adjust the button text based on permissions.
+* PDF title
+* Download button text
-* **PDF Title**: This is displayed at the top of the screen when users visit the PDF page. It also determines the filename of the downloaded PDF.
-* **Button Text**: This controls the label shown on the PDF download button.
+Both fields support bindings, so you can make the output depend on the current user or app context.
-These fields help tailor the user experience and provide context-specific naming for your reports.
+## Notes
-
\ No newline at end of file
+* The PDF title also becomes the filename of the downloaded file
+* Use the same layout patterns you would use on a regular screen
+* Keep the report layout simple if you want the exported PDF to stay readable
diff --git a/docs/Data/rest/index.md b/docs/Data/rest/index.md
index fec63aa89..3a8f925cc 100644
--- a/docs/Data/rest/index.md
+++ b/docs/Data/rest/index.md
@@ -26,12 +26,12 @@ next:
slug: rest-variables
title: REST Variables
---
-Budibase REST integrations are configured from **Workspace Settings > Connections > APIs**.
+Use the REST data source when you want Budibase to connect to an external API.
-A REST integration has two layers:
+REST setup has two parts:
-* **Connection-level config** in Settings (auth, base URL, shared headers/variables)
-* **Query-level config** in API Editor (method, path, body, bindings, pagination, transformer, projects)
+* **Connection settings** in **Workspace Settings > Connections > APIs**
+* **Query settings** in the API Editor
## Create a REST connection
@@ -46,7 +46,7 @@ A REST integration has two layers:
7. Add any required shared headers/URL params/static variables
8. Click **Save**
-## Connection tabs reference
+## Connection tabs
| Tab | Purpose | Typical fields |
| :-- | :-- | :-- |
@@ -54,23 +54,21 @@ A REST integration has two layers:
| Credentials | Shared request defaults | Base URL, URL parameters, headers, static variables |
| Advanced | Transport behavior | Reject unauthorized, Download images |
-## Open the API Editor and create your first query
+## Create a query
-You can start query creation from either path below:
-
-1. In the API builder sidebar, click **New API** to start a draft query
-2. In Settings, open a saved connection and click **Open in API Editor**
-3. Select or create the connection for your draft query
-4. Set method and endpoint path
-5. Add required params/headers/body
-6. Pick auth config if needed
+1. Open the API builder sidebar and click **New API**
+2. Or open an existing connection and click **Open in API Editor**
+3. Select or create the connection
+4. Set the method and endpoint path
+5. Add any required params, headers, body fields, or bindings
+6. Select an auth config if needed
7. Click **Send**
-8. Review response and schema
+8. Review the response and schema
9. Click **Save Query**
## Common migration notes (old to new UI)
-* Older docs and workflows may reference **APIs** or **datasources** from legacy menus
+* Older docs may reference **APIs** or **datasources** from legacy menus
* Current workflow for REST setup starts in **Settings > Connections > APIs**
* Query authoring still happens in the API Editor via **Open in API Editor**
diff --git a/docs/Data/rest/rest-authentication.md b/docs/Data/rest/rest-authentication.md
index 138678783..34c5d96db 100644
--- a/docs/Data/rest/rest-authentication.md
+++ b/docs/Data/rest/rest-authentication.md
@@ -10,7 +10,7 @@ metadata:
next:
description: ''
---
-REST authentication is configured at the **connection** level and selected at the **query** level.
+Configure REST authentication on the connection, then select it on each query.
## Supported auth types in Connections
@@ -21,28 +21,28 @@ REST authentication is configured at the **connection** level and selected at th
## Configure authentication on a connection
1. Open **Settings > Connections > APIs**
-2. Open your connection
+2. Open the connection
3. Go to **Authentication**
4. Click **Add authentication**
-5. Select auth type
-6. Fill required fields
-7. Click **Save**
+5. Select the auth type
+6. Fill in the required fields
+7. Save the auth config
You can store multiple auth configs on one connection.
## Select auth in a query
-1. Open connection in **API Editor**
-2. Open/create a query
+1. Open the connection in **API Editor**
+2. Open or create a query
3. Select auth config for the query
4. Click **Send**
-5. Save query
+5. Save the query
## Auth configuration guidance
-* Create separate auth entries per environment when needed.
-* Use clear names (`Prod Bearer`, `Staging OAuth2`).
-* Re-test all affected queries after editing a shared auth config.
+* Create separate auth entries per environment when needed
+* Use clear names such as `Prod Bearer` and `Staging OAuth2`
+* Re-test affected queries after editing a shared auth config
## Using SSO token bindings
diff --git a/docs/Data/rest/rest-bindings.md b/docs/Data/rest/rest-bindings.md
index b582b5b06..90f8f0920 100644
--- a/docs/Data/rest/rest-bindings.md
+++ b/docs/Data/rest/rest-bindings.md
@@ -10,13 +10,9 @@ metadata:
next:
description: ''
---
-Bindings pass runtime values into REST queries.
+Use bindings to pass runtime values into REST queries.
-Use bindings for values that vary by user, row, form input, URL parameter, or action context.
-
-## Binding syntax
-
-Use handlebars syntax:
+Bindings are useful for values that change by user, row, form input, URL parameter, or action context.
```handlebars
{{ bindingName }}
@@ -25,10 +21,10 @@ Use handlebars syntax:
## Create a binding in a query
1. Open a query in API Editor
-2. Add a value in URL/Params/Headers/Body using binding syntax
+2. Add a value in the URL, params, headers, or body using binding syntax
3. Define binding name and default value
-4. Click **Send** to test defaults
-5. Save query
+4. Click **Send** to test the default
+5. Save the query
## Where bindings can be used
@@ -38,9 +34,9 @@ Use handlebars syntax:
* Request body
* Transformer scripts
-## Example: filtered list query
+## Example
-Query goal: return records by status.
+Return records by status.
1. Create a `GET` query for the list endpoint
2. Add query param `status`
@@ -52,8 +48,6 @@ Query goal: return records by status.
## Passing bindings from forms
-Typical flow:
-
1. Add form fields
2. Configure button action **Execute query**
3. Map form values to query bindings
@@ -62,16 +56,16 @@ Typical flow:
## Binding design guidelines
-* Use descriptive names (`userId`, `status`, `startDate`).
-* Set safe defaults for optional values.
-* Avoid reusing one binding name for different concepts.
-* Validate string/number/date formatting expected by API.
+* Use descriptive names such as `userId`, `status`, and `startDate`
+* Set safe defaults for optional values
+* Avoid reusing one binding name for different concepts
+* Match the string, number, or date format expected by the API
## Troubleshooting
-* Binding appears literal in request: syntax not parsed correctly
-* Value missing at runtime: no input mapped in action
-* Wrong filter result: default value taking precedence unexpectedly
+* Binding appears literal in request: syntax is not parsed correctly
+* Value missing at runtime: no input is mapped in the action
+* Wrong filter result: the default value is taking precedence
## Related guides
diff --git a/docs/Data/rest/rest-example.md b/docs/Data/rest/rest-example.md
index ba2ff2ee5..698f7c1f9 100644
--- a/docs/Data/rest/rest-example.md
+++ b/docs/Data/rest/rest-example.md
@@ -6,17 +6,17 @@ hidden: false
metadata:
robots: index
---
-This tutorial builds a simple users app backed by a REST API with read/create/update workflows.
+This tutorial builds a simple users app backed by a REST API.
## Target architecture
* One REST connection (`Users API`)
* Four queries (`getUsers`, `getUser`, `createUser`, `updateUser`)
-* One table view + two side-panel forms
+* One table view and two side-panel forms
## Part 1: Prepare sample API
-1. Create a sample endpoint (for example MockAPI)
+1. Create a sample endpoint, such as MockAPI
2. Add fields:
* `id`
* `name`
@@ -27,10 +27,10 @@ This tutorial builds a simple users app backed by a REST API with read/create/up
## Part 2: Create connection
1. Open **Settings > Connections > APIs**
-2. Click **Add connection > Create custom**
-3. Set display name to `Users API`
-4. In **Credentials**, set **Base URL**
-5. Save
+2. Click **Add connection** and choose **Create custom**
+3. Set the display name to `Users API`
+4. In **Credentials**, set the base URL
+5. Save the connection
6. Click **Open in API Editor**
## Part 3: Create CRUD queries
@@ -46,7 +46,7 @@ For each query:
1. Set method and path
2. Configure bindings
-3. Configure body for write queries
+3. Configure the body for write queries
4. Click **Send**
5. Save query
@@ -70,9 +70,9 @@ For each query:
## Part 5: Edit existing user
-1. Add edit side panel with form (`name`, `address`)
+1. Add an edit side panel with a form for `name` and `address`
2. Set default values from selected-row state
-3. Add **Update** button
+3. Add an **Update** button
4. Button actions:
* Execute query `updateUser`
* Map `id`, `name`, `address`
@@ -82,9 +82,9 @@ For each query:
## Part 6: Create new user
-1. Add **Add new user** button
-2. Open create side panel with required fields
-3. Add **Save** button
+1. Add an **Add new user** button
+2. Open a create side panel with the required fields
+3. Add a **Save** button
4. Button actions:
* Execute query `createUser`
* Refresh table
@@ -95,15 +95,15 @@ For each query:
## Validation checklist
* Query tests return expected response codes
-* Table loads initial data
-* Edit flow updates row and refreshes UI
-* Create flow inserts row and refreshes UI
+* The table loads initial data
+* The edit flow updates the row and refreshes the UI
+* The create flow inserts the row and refreshes the UI
* Error responses surface user-visible notifications
## Common improvements
-* Add delete query/action (`DELETE /users/{{id}}`)
-* Add client-side search/filter
+* Add a delete query and action (`DELETE /users/{{id}}`)
+* Add client-side search and filtering
* Add form validation rules before execute-query actions
## Related guides
diff --git a/docs/Data/rest/rest-oauth2.md b/docs/Data/rest/rest-oauth2.md
index e9bb84b8a..11fcdb0aa 100644
--- a/docs/Data/rest/rest-oauth2.md
+++ b/docs/Data/rest/rest-oauth2.md
@@ -14,9 +14,9 @@ next:
slug: rest
title: REST API
---
-Budibase supports OAuth2 in REST connections using **Client Credentials**.
+Use OAuth2 when your REST connection needs machine-to-machine access.
-This is intended for machine-to-machine API access.
+Budibase supports OAuth2 in REST connections using **Client Credentials**.
## Required OAuth2 fields
@@ -33,20 +33,20 @@ This is intended for machine-to-machine API access.
## Configure OAuth2 in a connection
1. Open **Settings > Connections > APIs**
-2. Open/create a connection
+2. Open or create a connection
3. Go to **Authentication**
4. Click **Add authentication**
5. Choose **OAuth2** type
6. Enter required fields
-7. Save
+7. Save the auth config
## Use OAuth2 in queries
1. Click **Open in API Editor**
-2. Open/create query
-3. Select OAuth2 config in query auth settings
+2. Open or create a query
+3. Select the OAuth2 config in the query auth settings
4. Click **Send**
-5. Save query
+5. Save the query
## Example (Spotify)
@@ -59,10 +59,10 @@ Use your provider-issued client ID and client secret.
| Error | Typical cause | Action |
| :-- | :-- | :-- |
-| `invalid_client` | Bad client ID/secret | Re-copy credentials from provider |
+| `invalid_client` | Bad client ID or secret | Re-copy credentials from the provider |
| `unsupported_grant_type` | Wrong grant configured | Set Client Credentials |
-| `invalid_scope` | Scope format/value invalid | Match provider scope syntax |
-| API call still unauthorized | Wrong auth config selected in query | Re-select auth config and resend |
+| `invalid_scope` | Scope format or value invalid | Match the provider scope syntax |
+| API call still unauthorized | Wrong auth config selected in query | Re-select the auth config and resend |
## Related guides
diff --git a/docs/Data/rest/rest-pagination.md b/docs/Data/rest/rest-pagination.md
index 1843ce872..846191fa3 100644
--- a/docs/Data/rest/rest-pagination.md
+++ b/docs/Data/rest/rest-pagination.md
@@ -6,7 +6,7 @@ hidden: false
metadata:
robots: index
---
-Use pagination when an API returns list data in pages and you need reliable forward/backward navigation in app components.
+Use pagination when an API returns list data in pages.
## Before you start
@@ -24,23 +24,23 @@ Use pagination when an API returns list data in pages and you need reliable forw
## Configure query pagination
-1. Open query in API Editor
+1. Open the query in API Editor
2. Enable pagination
-3. Select pagination type and location
-4. Map request field names
-5. Map response field path for continuation token/page value
-6. Click **Send** repeatedly to validate progression
-7. Save query
+3. Select the pagination type
+4. Map the request field names
+5. Map the response field path for the next page value
+6. Click **Send** to validate progression
+7. Save the query
## Cursor-specific guidance
-* Map to the API field that returns the **next** cursor token
-* Verify token comes from raw response path expected by Budibase
-* Ensure stable sort order to avoid duplicates/missed rows
+* Map to the API field that returns the next cursor token
+* Verify the token comes from the raw response path expected by Budibase
+* Ensure stable sort order to avoid duplicate or missing rows
## Use paginated query in components
-1. Bind query (or data provider) to table/repeater
+1. Bind the query or data provider to a table or repeater
2. Enable component pagination controls where applicable
3. Validate first, next, previous, and final page behavior
@@ -48,10 +48,10 @@ Use pagination when an API returns list data in pages and you need reliable forw
| Symptom | Likely cause | Fix |
| :-- | :-- | :-- |
-| Always shows page 1 | Request page/cursor field not mapped | Re-check request field mapping |
-| Stops after first page | Response continuation path incorrect | Correct response field path |
-| Duplicate rows across pages | Non-deterministic API sorting | Add stable sort in API query |
-| Missing rows | Offset/page-size mismatch | Verify API math and defaults |
+| Always shows page 1 | Request page or cursor field not mapped | Re-check request field mapping |
+| Stops after first page | Response continuation path incorrect | Correct the response field path |
+| Duplicate rows across pages | Non-deterministic API sorting | Add stable sort in the API query |
+| Missing rows | Offset or page-size mismatch | Verify API math and defaults |
## Related guides
diff --git a/docs/Data/rest/rest-queries.md b/docs/Data/rest/rest-queries.md
index a065b2449..c86432bf4 100644
--- a/docs/Data/rest/rest-queries.md
+++ b/docs/Data/rest/rest-queries.md
@@ -10,7 +10,7 @@ metadata:
next:
description: ''
---
-REST queries are configured in API Editor for a saved REST connection.
+Create REST queries in the API Editor for a saved REST connection.
## Create a query
@@ -47,7 +47,7 @@ To protect your security, Budibase automatically sanitizes this preview:
| Area | What to configure | Notes |
| :-- | :-- | :-- |
| Method | `GET`, `POST`, `PUT`, `PATCH`, `DELETE` | Must match endpoint contract |
-| URL/Path | Endpoint path or full URL | Usually path + connection base URL. Full URLs require a protocol prefix and must match the datasource origin. |
+| URL/Path | Endpoint path or full URL | Usually path plus the connection base URL. Full URLs require a protocol prefix and must stay on the datasource origin. |
| Params | Query-string key/value pairs | Supports bindings |
| Headers | Request headers | Supports bindings and shared defaults |
| Body | Payload for write/query APIs | Use valid JSON/XML/Text as required |
@@ -66,9 +66,9 @@ Before first use:
Before production use:
-1. Test with realistic runtime bindings
-2. Validate empty-state response behavior
-3. Validate error-state handling in app actions
+1. Test with realistic runtime bindings.
+2. Validate empty-state response behavior.
+3. Validate error-state handling in app actions.
## Transformer example (flatten nested response)
@@ -92,14 +92,13 @@ After applying a transformer:
## Common issues
-* **Missing protocol**: Full URLs must include `http://` or `https://` to be valid. The **Send** button will be disabled and a warning displayed if the protocol is missing.
-* `401/403`: wrong or missing auth config.
-* `404`: wrong path or base URL.
-* `400`: REST query path must remain on the datasource origin. This occurs if an absolute URL or dynamic binding targets a different origin than the one configured in the datasource.
-* `400`: Redirect to a different origin is not permitted. Cross-origin redirects are blocked for security to prevent datasource credentials from being sent off-origin.
-* `400/422`: request payload does not match API contract.
-* Empty rows with `200`: binding values not populated as expected.
-* Schema mismatch in app: query changed but app bindings not updated.
+* **Missing protocol**: Full URLs must include `http://` or `https://` to be valid.
+* `401/403`: wrong or missing auth config
+* `404`: wrong path or base URL
+* `400`: the query must stay on the datasource origin. Cross-origin redirects are blocked.
+* `400/422`: request payload does not match the API contract
+* Empty rows with `200`: binding values were not populated as expected
+* Schema mismatch in app: the query changed but app bindings were not updated
## Related guides
diff --git a/docs/Data/rest/rest-query-import.md b/docs/Data/rest/rest-query-import.md
index 791957d7b..fef9323b8 100644
--- a/docs/Data/rest/rest-query-import.md
+++ b/docs/Data/rest/rest-query-import.md
@@ -10,7 +10,7 @@ metadata:
next:
description: ''
---
-Import lets you create multiple REST queries at once from API definitions.
+Use query import to create multiple REST queries at once from API definitions.
## Supported formats
@@ -28,7 +28,7 @@ Import lets you create multiple REST queries at once from API definitions.
* **File**: upload spec file
* **Raw text**: paste spec text
6. Click **Import**
-7. Review imported queries
+7. Review the imported queries
8. Open key queries and click **Send**
9. Save any adjusted queries
@@ -43,10 +43,10 @@ When importing from a URL, Budibase applies the same security hardening and vali
1. Rename generic query names
2. Confirm auth config per query
-3. Confirm base URL/path compatibility with your environment
+3. Confirm base URL and path compatibility with your environment
4. Add or correct bindings for runtime values
5. Confirm request body formats
-6. Validate schema output after first successful run
+6. Validate the schema output after the first successful run
## Importing from Postman
@@ -59,9 +59,9 @@ Postman collections should be converted to OpenAPI first.
## Common import issues
-* Unknown/unsupported fields in spec: simplify or clean OpenAPI before import
-* Imported query fails auth: map to correct connection auth config
-* Paths duplicate base URL: correct path/full URL handling
+* Unknown or unsupported fields in the spec: simplify or clean the OpenAPI file before import
+* Imported query fails auth: map to the correct connection auth config
+* Paths duplicate the base URL: correct the path or full URL handling
## Related guides
diff --git a/docs/Data/rest/rest-templates.md b/docs/Data/rest/rest-templates.md
index 048ca9b99..cf930d0cb 100644
--- a/docs/Data/rest/rest-templates.md
+++ b/docs/Data/rest/rest-templates.md
@@ -1,22 +1,25 @@
---
-title: REST Templates
+title: REST templates
+excerpt: Start REST connections from a template
deprecated: false
hidden: false
metadata:
+ title: ''
+ description: ''
robots: index
---
REST templates provide connection starters and allow you to reuse OpenAPI specifications across your workspace. You can find them under **Settings > APIs > API Specs**.
-Templates are useful when you want known endpoint structures, default headers, and static variables preloaded.
+Templates can include endpoint structure, default headers, static variables, and suggested auth.
## What templates usually include
Templates can provide:
* Base URL defaults
-* Suggested endpoint actions
-* Required static variables (for example owner/repo, project identifiers)
-* Suggested auth pattern
+* Suggested endpoints
+* Required static variables such as owner, repo, or project identifiers
+* Suggested auth patterns
* Template collections with child APIs for the same provider
Template content can still be edited after creation.
@@ -54,8 +57,8 @@ Once imported, your spec will appear in the **Imported API specs** section, wher
Some template collections include child APIs and use one of two connection modes:
-* **Shared**: child APIs share connection settings (for example base URL and auth). You choose the child API when creating or editing queries.
-* **Independent**: each child API is treated as its own connection choice. You select the child API when creating the connection.
+* **Shared**: child APIs share connection settings such as base URL and auth
+* **Independent**: each child API is treated as its own connection choice
## Validate the template connection
@@ -69,10 +72,10 @@ Some template collections include child APIs and use one of two connection modes
## Troubleshooting template setup
* `401/403` after template import: auth is missing or invalid
-* `404` on template query: base URL/path mismatch for your target service
-* Empty response: required bindings not set or wrong defaults
-* Template variable locked/required: ensure value exists in **Credentials > Static Variables**
-* Missing endpoint list for a child API: confirm the correct child API is selected for the connection or query
+* `404` on a template query: the base URL or path does not match the target service
+* Empty response: required bindings are missing or have the wrong defaults
+* Template variable locked or required: ensure the value exists in **Credentials > Static Variables**
+* Missing endpoint list for a child API: confirm the correct child API is selected
## When to use Create custom instead
diff --git a/docs/Data/rest/rest-variables.md b/docs/Data/rest/rest-variables.md
index 8f01eb850..444a4cc54 100644
--- a/docs/Data/rest/rest-variables.md
+++ b/docs/Data/rest/rest-variables.md
@@ -10,29 +10,29 @@ metadata:
next:
description: ''
---
-REST variables let you reuse values across multiple queries in the same connection.
+Use REST variables to reuse values across multiple queries in the same connection.
## Variable types
* **Static variables**: manually defined constant values
-* **Dynamic variables**: values extracted from response headers/body
+* **Dynamic variables**: values extracted from response headers or body data
## Static variables
Use static variables for shared constants:
* API version
-* Host/tenant identifiers
-* Shared language/region codes
+* Host or tenant identifiers
+* Shared language or region codes
* Reused query fragments
### Create static variables
1. Open **Settings > Connections > APIs**
-2. Open your connection
+2. Open the connection
3. Go to **Credentials**
4. Add entries under **Static Variables**
-5. Click **Save**
+5. Save the connection
## Dynamic variables
@@ -40,25 +40,25 @@ Use dynamic variables to chain queries.
Common patterns:
-* Auth query returns token used by later queries
-* Lookup query returns ID used by update/delete query
+* Auth query returns a token used by later queries
+* Lookup query returns an ID used by an update or delete query
* Cursor response field reused for pagination calls
### Create dynamic variables
-1. Open connection in API Editor
+1. Open the connection in API Editor
2. Run a source query
-3. Create dynamic variable from response header or schema field.
-4. Name the variable clearly.
-5. Save query changes.
+3. Create a dynamic variable from a response header or schema field
+4. Name the variable clearly
+5. Save the query changes
### Edit dynamic variables
-1. Open connection in **Settings > Connections > APIs**.
-2. Go to **Credentials**.
-3. Open **Dynamic Variables**.
-4. Update expression/path.
-5. Save and retest downstream queries.
+1. Open the connection in **Settings > Connections > APIs**
+2. Go to **Credentials**
+3. Open **Dynamic Variables**
+4. Update the expression or path
+5. Save and retest downstream queries
## Expression examples
@@ -87,9 +87,9 @@ Use **bindings** when values are supplied per execution.
## Common issues
-* Variable resolves empty: source query response shape changed.
-* Variable points to object instead of scalar: expression path too broad.
-* Query retries/fails with variable errors: source variable query not returning expected value.
+* Variable resolves empty: the source query response shape changed
+* Variable points to an object instead of a scalar: the expression path is too broad
+* Query retries or fails with variable errors: the source variable query is not returning the expected value
## Related guides
diff --git a/docs/Data/sql-datasource.md b/docs/Data/sql-datasource.md
index 9910f9fb0..01d9c9e42 100644
--- a/docs/Data/sql-datasource.md
+++ b/docs/Data/sql-datasource.md
@@ -19,6 +19,8 @@ Our core SQL datasource connections include:
* [Oracle](doc:oracle)
* [PostgreSQL](doc:postgresql)
+For the shared workflow after connecting, see [Working with SQL datasources](doc:sql-workflow).
+
{`
`}
diff --git a/docs/Data/sql-workflow.md b/docs/Data/sql-workflow.md
new file mode 100644
index 000000000..065b73cb8
--- /dev/null
+++ b/docs/Data/sql-workflow.md
@@ -0,0 +1,83 @@
+---
+title: Working with SQL datasources
+excerpt: Fetch tables, define relationships, and build screens from SQL databases
+deprecated: false
+hidden: false
+metadata:
+ title: ''
+ description: ''
+ robots: index
+next:
+ description: ''
+---
+Use this workflow after you connect a SQL datasource.
+
+## Connect the datasource
+
+1. Open the **Data** section
+2. Add a new source
+3. Choose your SQL provider
+4. Enter the connection details
+5. Fetch the tables you need
+
+See the provider page for connection-specific setup:
+
+* [PostgreSQL](doc:postgresql)
+* [MySQL / MariaDB](doc:mysql-mariadb)
+* [MS SQL Server](doc:ms-sql-server)
+* [Oracle](doc:oracle)
+
+## Fetch tables
+
+Fetch only the tables you need. This keeps the schema easier to manage and reduces setup later.
+
+## Define relationships
+
+Add relationships for the joins you want to reuse in screens and bindings.
+
+Use:
+
+* One-to-many for foreign keys
+* Many-to-many when you have a join table
+
+If Budibase rejects a join table, fix the source constraints in the database and fetch the table again.
+
+## Build screens
+
+Once the tables and relationships are ready, create autogenerated screens from the datasource.
+
+Use them to:
+
+* Browse related records
+* Open detail or edit views
+* Filter by selected values
+* Reuse relationships in bindings and repeaters
+
+## Custom queries
+
+Use a custom query when the data shape is easier to express in SQL than in the screen builder.
+
+Custom queries are useful for:
+
+* Flattening nested relationships
+* Grouped or aggregated results
+* Write workflows from a form action
+* Returning a shape that is easier to bind in design
+
+For read operations, select `Read` when you want to use the query with a data provider.
+
+For write operations, use `Execute Query` from a form or button action.
+
+## Query bindings
+
+Add bindings when the query needs dynamic values.
+
+Use the query settings drawer to override defaults at runtime.
+
+## Related guides
+
+* [SQL datasource](doc:sql-datasource)
+* [PostgreSQL](doc:postgresql)
+* [Screens](doc:screens)
+* [Data providers](doc:data-provider)
+* [Bindings](doc:bindings)
diff --git a/docs/Data/transformers.md b/docs/Data/transformers.md
index 978eed97d..5141c004d 100644
--- a/docs/Data/transformers.md
+++ b/docs/Data/transformers.md
@@ -1,6 +1,6 @@
---
title: Transformers
-excerpt: Using Budibase query transformers
+excerpt: Shape query results before they reach your app
deprecated: false
hidden: false
metadata:
@@ -10,100 +10,62 @@ metadata:
next:
description: ''
---
-In this section, we will cover how to use Budibase data transformers, which can be used as part of data source queries. It is often a requirement when retrieving data from various sources to transform to fit your app's use case - from simply extracting properties from deeper JSON objects to enriching your data with more information transformers can be used for a wide variety of applications.
+Use a transformer when a query returns data in a shape that needs to be simplified or reshaped before it reaches your app.
-> 📘 This section requires JavaScript knowledge
->
-> We recommend the Modern [JavaScript Tutorial ](https://javascript.info/)to pick up the basics, for transformers you will specifically benefit from knowledge around data types, such as [Arrays](https://javascript.info/array), [Array Methods](https://javascript.info/array-methods) and [Objects](https://javascript.info/keys-values-entries).
+Transformers are written in JavaScript.
-To create a transformer first you need to create an external data source and a query, steps for this can be found in the [External Data Sources](doc:data-sources) section.
+## What transformers receive
-## Accessing bindings
+Transformers can access:
-It is possible to access the bindings that your query received when it was called, through the `params` object that is available in the transformer context. For example if I've added a binding of `lastName` then I can access this property in the transformer under `params.lastName`. You can see a full example of this below.
+* `data` - the query results
+* `params` - the bindings passed to the query
-
+## Create a transformer
-## Tutorial
+1. Create a datasource and query
+2. Open the transformer editor for the query
+3. Write JavaScript to reshape the response
+4. Send the query to test the output
+5. Save the query
-In the example below we will be transforming some data from the [Open Brewery Database](https://www.openbrewerydb.org/) - we will be creating an app that has tallies of the number of breweries by US state.
+## Return shape
-When you first create a query you'll see the transformer code editor in its own section, as seen below.
+A transformer must return an array.
-
+If you return a single object, Budibase wraps it as one row.
-> 📘
->
-> There are two properties that are accessible by default within the transformer. First, the data which as its name suggests contains the data retrieved by the query. Secondly, params, which contain the query bindings/parameters that were provided when it was called.
+If you return an array of values, Budibase treats each value as a row.
-This will return the data exactly the way it is retrieved from the data source, it is recommended to get your query up and running correctly first before altering your transformer, this will allow you to look at the initial schema of the data returned. In our example so far we have:
+## Example
-1. Setup a REST data source, with our URL set to
+Use a transformer to count breweries by state.
-```
-https://api.openbrewerydb.org
-```
-
-2. Created a query and set the path to *breweries*
-3. Ran the query to see the schema
-
-The schema for this query appears as below:
-
-
-
-Using this information we can now write the transformer function that will be used. Initially we just want to write a function which will:
-
-1. Work through the array of breweries that are returned by the API, in the format shown above
-2. Extract the state and add it to a total count of states that have been seen
-3. Return a new structure that contains only the state name and the count of breweries within
-
-You can see the basic transformer we have written to do this below.
-
-
-
-Here we have taken the data, written a for loop that iterates through a fills up an object with counts for each state (using the state's name as the key into the object), and finally, we've mapped these totals to our output structure, an object with a state and count property. You can see from this that we can drastically change the format of the data; using JavaScript you can change the data in a multitude of ways.
+```javascript
+const counts = {}
-For the last part, we want to add data that simply isn't a part of the query, enriching the application logic that we've provided as part of the transformer. We are going to add a URL that points to an image of the states flag for each of the state entries, to do this we will create a URL dynamically to [http://flags.ox3.in/](http://flags.ox3.in/) - a repository of SVG flags. You can see the final function which does this below.
-
-
-
-This may look a little complicated, but all we have done is added a map of the state names to state codes, then at the end when we produce the final data we lookup the state name in the map and build a URL that has the code in it. You can see in our results section we now have a "flag" property, which can be used in the design section with something like an image or card component. We've thrown together a quick example of how this can all come together in your design, using only a data provider, repeater and card component bound to the "state", "count" and "flag" properties our transformer produced.
-
-.png")
-
-Hopefully, this has helped to demonstrate how transformers can be used to get the data you need for your application, happy coding!
-
-## Response
-
-A query must always return an array as that is how Budibase handles data, it will always attempt to fit your data into a column/row type table structure. If you have an endpoint which returns a single object then the following conversion with occur:
-
-```json
-{
- "a": 1,
- "b": 2,
+for (const brewery of data) {
+ const state = brewery.state
+ counts[state] = (counts[state] || 0) + 1
}
-// Budibase will convert this to:
-[
- { "a": 1, "b": 2 }
-]
+return Object.entries(counts).map(([state, count]) => ({
+ state,
+ count,
+}))
```
-This functionality is important to understand when building transformers as if you return an array it will be broken up into individual rows. An example of this would be the use of the `Object.keys` or `Object.values` functions, breaking an object up into an array of keys/values. Below we can see an example of how Budibase would treat a transformer which returns the direct result of these functions:
+You can also add derived fields, such as a flag URL, before returning the final rows.
-```json
-// outputs [ "a", "b" ]
-return Object.keys({ "a": 1, "b": 2 })
-
-// Budibase will break this array into the following response:
-[
- { "value": "a" },
- { "value": "b" },
-]
-```
+## Common uses
-If we were to take the above example and add it to a table within Budibase it would appear as seen below.
+* Flatten nested API responses
+* Count or group records
+* Add derived fields
+* Convert API data into a table-friendly shape
-
+## Notes
-If you instead would like to return all of these elements as an array you can do this, by wrapping the transformer response in an object, like `return { letters: Object.keys({ "a": 1, "b": 2 }) }` - however the query will still return an array, so that it can be used by a data provider + table/repeater.
+* Keep the query working first, then refine the transformer
+* Use `params` for binding values passed into the query
+* Re-test the query after changing the transformer
diff --git a/docs/Develop & Scale/custom-plugin/custom-automations.md b/docs/Develop & Scale/custom-plugin/custom-automations.md
index 3bcd95f86..95baa4d40 100644
--- a/docs/Develop & Scale/custom-plugin/custom-automations.md
+++ b/docs/Develop & Scale/custom-plugin/custom-automations.md
@@ -10,97 +10,110 @@ metadata:
next:
description: ''
---
-## Purpose
+Custom automation steps let you extend Budibase automations with your own backend logic.
-Automation action plugins can be used to extend the functionality of automations. [Automations](https://docs.budibase.com/docs/automation-steps) can be used in conjunction with the various triggers Budibase provides, like webhooks or row created/updated/deleted events to create custom backend functionality, API endpoints or background processes. With plugins we can extend this further, for example we could build an automation plugin that integrates with a local IoT system, allowing you to trigger real world actions through Budibase. Currently, automation plugins can only be used in self host.
+Use them when you need an action that is not covered by the built-in automation steps.
-Datasource plugins can also be used to provide similar functionality through the "External Data Connector" action in automations - however the integration with automations is more complex, the plugin is more complex (requiring [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations to be fully defined) and is more data centric in functionality; automations in contrast are more action focused.
+## Before you start
-## Getting started
+Make sure you have:
-Make sure you have the latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup) installed. Even if you have previously installed the CLI, you may want to install it again to be sure you have the latest version.
+* The latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup)
+* Node.js `v20` or later
+* A self-hosted Budibase installation
-Also make sure that you have **node v20+** installed. You can run `node --version` to see if you need to upgrade.
+Automation step plugins are not available in Budibase Cloud.
-Please note that automation action plugins can only be used in self host installations - they cannot be imported to the Budibase Cloud.
+## Create the step
-Once that's done, we need to initialise a template for our automation action.
-
-> 📘 Automation actions - not triggers
->
-> It is important to note that you can only build automation actions, triggers are built into Budibase and can only be extended through the core project. If you need a new automation trigger type please create an [issue](https://github.com/Budibase/budibase/issues/new?assignees=\&labels=enhancement\&template=feature_request.md\&title=) describing what you need.
-
-Navigate to the folder location you want to create your plugin, and execute the following command:
+1. Go to the folder where you want to create the plugin.
+2. Run:
```shell
budi plugins --init automation
```
-Next you will receive a series of prompts to enter details about your automation. You can press enter without providing a value if you are happy to use the placeholder.
+3. Complete the setup prompts.
+4. Open the generated project in your editor.
-
+The template includes:
-This will create an automation skeleton project, with the following structure:
+* `src/index.ts` for the entry point
+* `src/automation.ts` for the step implementation
+* `schema.json` for builder configuration
+* `test/index.spec.ts` for template tests
-```Text YAML
-src:
- index.ts: The main entry file - this should not change under normal circumstances.
- automation.ts: The code for your custom automation action. Do not rename.
+## Build and watch
-test:
- index.spec.ts: A Jest test file that includes a template test for the action runner.
+Build the project with:
-package.json: The version, license and dependencies for your automation action.
-
-schema.json: The metadata which describes your automation action - like settings displayed in builder.
+```shell
+cd budibase-automation
+yarn build
```
- There will be a lot of generated files/directories, such as `node_modules` and build/test configuration files - you will not usually need to edit these, if you do need to change these it may be worth raising an [issue](https://github.com/Budibase/budibase/issues/new?assignees=\&labels=enhancement\&template=feature_request.md\&title=).
+During development, run `yarn watch` so changes are reflected in your local Budibase instance.
-## Building your automation
+If you change `schema.json`, refresh the builder so the new action shape appears.
-After you have created your new automation action directory, execute the following:
+## Implement the step
-```shell
-cd budibase-automation
-yarn build
-```
+`automation.ts` exports a single `run` function.
+
+Use it to:
+
+* Read inputs from prior automation steps
+* Perform the required action
+* Return the next output for the automation chain
+
+Keep the action focused on one responsibility. If you need multiple behaviours, create separate steps.
+
+The `run` function receives the automation context, step inputs, and an event emitter. Use those to read data, return outputs, or trigger downstream behaviour.
+
+The template also includes a `stepId`. Keep that identifier stable once the step is in use, because changing it can break existing automations.
+
+## Configure the schema
-Assuming you have [updated the server env variable](https://docs.budibase.com/docs/custom-plugin#hot-reloading-developers-only) you should now be able to see your automation plugin in Budibase, as a selectable automation step.
+`schema.json` defines:
-### Automation.ts
+* The step name and icon
+* The inputs the user configures in the builder
+* The outputs the step returns
-This is where the code will live for your automation action. It is recommended that it is developed in TypeScript, this allows use of the [@budibase/types](https://www.npmjs.com/package/@budibase/types) package if you desire access to any of the Budibase types.
+Keep the public configuration narrow and explicit.
-To make sure your saved changes are passed through to your Budibase server, you must execute a `yarn watch` within your custom automation project. This will allow your local Budibase app builder to see those changes in real-time, which is very handy for getting your automation to behave just right. Please note that if you change the `stepId` in your `schema.json` then any automations currently using the plugin will break, the `stepId` should not change.
+For most actions, set `type` to `action`. Use a logic step only when the plugin is meant to control flow rather than perform an action.
-#### Methods
+Use `inputs` for defaults and `schema.inputs` and `schema.outputs` for the structured contract that the builder and runtime share.
-The `automation.ts` file contains a single exported function - `run`. This takes the inputs from the prior automation steps, as configured by the user and performs the steps required for this action. This can be quite daunting, as you can do anything with this function, for some inspiration as to how this can be used you can look through the [built-in actions](https://github.com/Budibase/budibase/tree/develop/packages/server/src/automations/steps).
+## Testing
-Types involved with the automation runner can be found [here](https://github.com/Budibase/budibase/blob/develop/packages/types/src/documents/app/automation.ts). Importantly the `AutomationStepInput` which is provided to the automation runner, contains the following:
+Use `test/index.spec.ts` to verify the step behaves correctly with representative inputs.
-| Property | Type | Description |
-| :-------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `inputs` | object | A map of the specific inputs the user has specified for this automation, these may have been extracted from context |
-| `context` | object | The full automation context, containing the outputs of all prior steps |
-| `emitter` | object | An event emitter, used to trigger other events in Budibase (can trigger other automations) |
-| `appId` | string | The app ID which this automation is being ran within; can be used with the Budibase API, or functions in [@budibase/backend-core](https://npmjs.com/package/@budibase/backend-core) |
+Focus on:
-### Test
+* Input handling
+* Returned outputs
+* Error paths
+* Any side effects triggered by the step
-The `index.spec.ts` test file includes a template unit test for the runner function. testing can be quite simple, just pass in the require inputs and check that the function returns the expected outputs/performs the expected actions; the design of your test will depend on the function of your automation action.
+When the action interacts with external services, add tests for auth failures and invalid input as well as the happy path.
-Testing is not required for developing an automation plugin, but can be useful when developing, rather than having to run an automation within Budibase, you can check your automation action performs its expected result through a test.
+## Add an icon
-### Schema
+Pick an icon from the [Phosphor icons](https://phosphoricons.com/) set, then set the icon name in `schema.json`.
-The schema defines various properties such as the name, icon and the inputs/outputs of your automation action.
+Keep the icon name stable once the step is in use, because changing it can affect existing automations.
-You can jump to the automation schema page [here](https://docs.budibase.com/docs/automation-schema#schema-structure).
+## Development notes
-### Adding an icon
+* Automation step plugins are action focused
+* They are only available in self-hosted Budibase
+* `stepId` stability matters more than the display name
+* The built-in trigger types are not extended through this plugin path
-Adding an icon to your automation action is quite simple, first pick the icon you would like to use on the [Phosphor Icon](https://phosphoricons.com/) site. For example if we you want to use the "list-magnifying-glass" icon shown [here](https://phosphoricons.com/?q=list+magnifying+glass).
+## Related guides
-Then all you need to do is update your `schema.json` - find the top level `icon` field and set it to `list-magnifying-glass` - this is case sensitive and should not contain spaces.
+* [Automation schema](https://docs.budibase.com/docs/automation-schema#schema-structure)
+* [Budibase CLI setup](https://docs.budibase.com/docs/budibase-cli-setup)
+* [Custom plugin overview](https://docs.budibase.com/docs/custom-plugin)
diff --git a/docs/Develop & Scale/custom-plugin/custom-component.md b/docs/Develop & Scale/custom-plugin/custom-component.md
index 98395f148..b1efc9a47 100644
--- a/docs/Develop & Scale/custom-plugin/custom-component.md
+++ b/docs/Develop & Scale/custom-plugin/custom-component.md
@@ -10,599 +10,136 @@ metadata:
next:
description: ''
---
-{`
-
-`}
+Custom components let you extend the Budibase builder with your own Svelte-based UI elements.
-## Getting started
+Use them when the built-in component set does not cover the layout or interaction you need.
-Budibase components are written in [Svelte](https://svelte.dev/).
+## Before you start
-If you are using VSCode for your development, you will most likely want to install the [Svelte plugin](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) to enable syntax highlighting.
+Make sure you have:
-> 📘 NodeJS version
->
-> Make sure you have node v20+ installed. You can run `node --version` to see if you need to upgrade.
+* The latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup)
+* Node.js `v20` or later
+* A local development environment for Budibase plugins
-### Initialise component
+## Create the component
-Make sure you have the latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup) installed. Even if you have previously installed the CLI, you may want to install it again to be sure you have the latest version.
-
-Once that's done, we need to initialise a template for our component.
-
-Navigate to the folder location you want to create your plugin, and execute the following command:
+1. Go to the folder where you want to create the plugin.
+2. Run:
```shell
budi plugins --init component
```
-Next you will receive a series of prompts to enter details about your component. You can press enter without providing a value if you are happy to use the placeholder.
-
-
-
-Within the newly created custom component project you should see a tree structure as follows:
+3. Complete the setup prompts.
+4. Open the generated project in your editor.
-
+The template includes:
-
+* `Component.svelte` for the component UI
+* `schema.json` for the builder settings
+* `test/index.spec.ts` for template tests
-## Building your component
+## Build and watch
-After you have created your new plugin directory, execute the following:
+Build the project with:
-```shell Shel
+```shell
cd my-component
yarn build
```
-Assuming you have [updated the server env variable](https://docs.budibase.com/docs/custom-plugin#hot-reloading-developers-only) you should now be able to see your component template in Budibase:
-
-
-
-
-
-### Component.svelte
-
-This is where your svelte code will live for your component. You can use html tags, css styling and bindings as you would for any other svelte component.
-
-To make sure your saved changes are passed through to your Budibase server, you must execute a `yarn watch` within your custom component project. This will allow your local Budibase app builder to see those changes in real-time, which is very handy for getting your component to look and behave just right.
-
-> 🚧
->
-> The `use:styleable={$component.styles}` attribute must be included in your top-level component div. Removing this will cause issues when displaying your component.
-
-
-
-#### SDK Context
-
-The SDK context gives you access to the internal Budibase API.
-
-```javascript
-const { styleable, API } = getContext("sdk")
-```
-
-The [packages/server/src/api/routes](https://github.com/Budibase/budibase/tree/develop/packages/server/src/api/routes) directory provides all of the available endpoint definitions, and the implementations can be found in the [packages/server/src/api/controllers](https://github.com/Budibase/budibase/tree/develop/packages/server/src/api/controllers) directory.
-
-This provides a large amount of integration with various aspects of the Budibase platform.
-
-
-
-#### Context
-
-The context key is a promise of what data bindings the component is going to provide. The builder will use this information to generate data bindings to display, but it's up to the components themselves to provide the data they say they will.\
-Components can provide many contexts, so the value of the context key may be either an object or an array of objects.
-
-Each context definition supports the following configuration:
-
-
-
-
-
- Key
-
-
-
- Description
-
-
-
- Possible values
-
-
-
-
-
-
-
- type
-
-
-
- The type of context provided
-
-
-
- **static**\
- Static contexts define an array of explicit bindings which are provided by the component. This is used when you know each what data bindings your component provides.
-
- **schema**\
- Schema contexts are generated dynamically based on settings. The data bindings provided will be the schema of a certain datasource. How this datasource is determined is by looking at the component settings and identifying a setting of type dataSource, table or schema. If the component has a setting of type `dataProvider`, the data provider stack will be traversed upwards until a matching setting is found. Data bindings are then generated for whatever matching datasource is found. The component is expected to provide some sort of row from some datasource if this context setting is used.
-
-
-
-
-
- values
-
-
-
- An array of objects defining which static bindings are available
-
-
-
- An array of objects containing `key`, `label` and `type` properties.
-
- Defines the readable and runtime values for the binding, in the label and key fields respectively.
-
-
-
-
-
-Example context:
-
-```json Context
-[
- {
- "type": "static",
- "values": [
- {
- "label": "Row index",
- "key": "index",
- "type": "number"
- }
- ]
- }
-]
-```
-
-
-
-### schema.json
-
-On the next page we will discuss the intricacies of the component schema.json file - which defines the inputs and outputs of your component, which will be controlled through the component settings panel in the builder.
-
-You can jump to the component schema page [here](/docs/component-schema).
-
-
-
-### Using TypeScript
-
-The Budibase svelte components use JavaScript by default. If needed, you can convert your custom component over to TypeScript by following the **Adding TypeScript to an existing project** section of this blog: [https://svelte.dev/blog/svelte-and-typescript](https://svelte.dev/blog/svelte-and-typescript).
-
-In addition, you may need to install **tslib** i.e. `yarn add tslib`, and add an `index.ts` file.
-
-An example of a custom component that was ported over can be found in this [TypeScript date-picker](https://github.com/melohagan/budibase-component-kasper-date-picker).
-
-
-
-## Example: Custom form field component
-
-In this example we will create a Star Rating component that can be added as a field to Budibase Forms.
-
-> 📘
->
-> This example is taken from the [Star Rating custom component](https://github.com/andz-bb/budibase-component-star-rating)
-
-This example assumes you already have an empty component [initialised and built.](#getting-started) We're going to focus on the parts that allow this custom component to work as an input within the existing Form component.
-
-### Updating schema.json
-
-The first step will be to define the settings that will be available to our form. The most important setting for any form field component is the field that we want the input to be for. In my case, I have a Number column in one of my tables called Rating that I want to be able to use my Star Rating component with.
-
-We need to add a setting for Field to our component and we can do this by defining it in schema.json. On an empty component template there is a placeholder setting for Text. We will replace this with a setting for Field which will be of type `field/number` and make it a required field. While we're here we can also add a text field for the label.
-
-
- component settings within schema.json
-
-
-
- the new settings for our component now show up in the builder
-
-
-#### Optional: Adding custom validation
-
-If you would like the user to be able to add component level validation, you can add an validation type settings object to your settings array. We will use this `validation` object later [when registering the form field.](#integrating-with-the-form-component)
-
-```json schema.json
- {
- "type": "validation/number",
- "label": "Validation",
- "key": "validation"
- }
-```
-
-
-
-You can view [my full schema with extra settings here](https://github.com/andz-bb/budibase-component-star-rating/blob/master/schema.json) or [view documentation for the component schema here.](/docs/component-schema)
-
-### Working with Component.svelte
-
-The plugin template comes with a basic placeholder in `Component.svelte` and this is what we will modify to create our custom component. This is written in Svelte - if you are new to it you can [check out the tutorial here.](https://svelte.dev/tutorial/basics)
-
-The first thing we'll do is declare props for our settings values we created above. The syntax for this is `export let [your-setting-key];` This means we can use the parameters set in the builder in our component.
-
-Now that our props are added we can continue to build out the front end. Using JS, HTML and CSS we can create the basic interface for our component and then make it functional.
-
-
- looks good, now let's make it useful!
-
-
-### Integrating with the Form component
-
-The first step here is to get the context from the parent Form component. We can then use that Form Context to register our component as a Field in that Form.
-
-```javascript Component.svelte
-export let field;
-export let label;
-export let validation;
-
-const component = getContext("component");
-const formContext = getContext("form");
-const formStepContext = getContext("form-step");
-
-const formApi = formContext?.formApi;
-$: formStep = formStepContext ? $formStepContext || 1 : 1;
-$: formField = formApi?.registerField(field, "number", 0, false, validation, formStep);
-```
-
-> 📘 formApi.registerField() parameters from our example
->
-> * field name - we are using the value of `field` from our props
-> * field data type - in this case, the field is for a `number`
-> * field default value - we have set that to be `0` here
-> * field disabled - ours is `false` here but you could make it configurable
-> * field validation - ours is `validation` from props, but can be null if no validation is required
-> * form step - we use `formStep` derived from the formStepContext
-
-As this is a form component, we need to verify that the component is actually being used as a child of a form in our Budibase screen. To do this, I will add a Svelte if/else statement to our component so that it will render our field if it's the child of a form, or show a warning if it's not.
-
-```javascript Component.svelte
- {#if !formContext}
-
Form components need to be wrapped in a form
- {:else}
- // our component goes here
- {/if}
-```
-
-### Setting the field value
-
-Now that the field is registered, we have to set it to update the form's properties in the parent component every time the store changes. Using [onDestroy](https://svelte.dev/docs#run-time-svelte-ondestroy) we can deregister our field from our form whenever the component unmounts.
-
-```javascript Component.svelte
-import { onDestroy } from "svelte";
-
-let fieldApi;
-let fieldState;
-
-$: unsubscribe = formField?.subscribe((value) => {
- fieldState = value?.fieldState;
- fieldApi = value?.fieldApi;
-});
-
-onDestroy(() => {
- fieldApi?.deregister();
- unsubscribe?.();
-});
-```
-
-Just like that we have everything we need to set a value to our form using our component.
-
-Consider the **Icon** svelte component:
-
-```typescript Icon.svelte
-= i + 1}
- on:click={() => fieldApi?.setValue(i + 1)}
-/>
-```
-
-We determine the how many stars should be filled from `fieldState.value`, and we can set the number of stars in the *on click* event via `fieldApi.setValue('new value here')`.
-
-### Integrating with Field Groups
-
-Remember we made a setting for the label above? Now we are going to make our component compatible with the Budibase Field Group component, so the label styling can come from that. In order to make this work we need to get context from the Field Group, just like we did above for the Form component.
-
-```html Component.svelte
-
-
-
-
-{/if}
-
-
-```
-
-
-
-## Example: Context provider
-
-Sometimes you want to expose [bindings](https://docs.budibase.com/docs/bindings) to child components without the need of a form component - you do this through [context](https://docs.budibase.com/docs/custom-component#context).
-
-In this example we will add a toggle component that can provide its boolean state to child components. The codebase can be found here: [budibase-toggle](https://github.com/YuanZhang98/budibase-toggle).
-
-### Add context to schema.json
-
-First we need to tell Budibase that this component can take children. We do this with the *hasChildren* property in the `"schema"` object.
-
-```json
-{
- "schema": {
- "name": "budibase-toggle",
-
- "hasChildren": true
- ...
-```
-
-Next provide the context array after the `"settings"` property. The context array provides a list of bindings that will be available to the children.
-
-```json
-{
- "hasChildren": true,
- "settings": [..],
- "context": [
- {
- "type": "static",
- "values": [
- {
- "label": "Active State",
- "key": "isActive",
- "type": "boolean",
- "defaultValue": false
- }
- ]
- }
- ]
- ...
-```
-
-If you now build and import the component, you should see something similar to the following:
+During development, run `yarn watch` so your local Budibase instance picks up changes as you edit.
-
+If you update `schema.json`, refresh the builder so the new settings and bindings appear.
-### Implement context binding
+## Implement the component
-Whilst we have exposed the context, we need to do bind it to our toggle state:
+`Component.svelte` is the main entry point for the component UI.
-1. Import the **Provider** from the SDK:
+Use it to define:
-```javascript
-const { styleable, Provider } = getContext("sdk")
-```
-
-2. Next bind the *dataContext* to the toggle state of the component:
-
-```javascript
-let isActive = false;
-$: dataContext = {
- isActive
-}
-```
-
-3. Add the Provider and slot:
-
-```html
-
-
-
...
-
-
-
-```
+* Layout and markup
+* Styling
+* Builder bindings and props
-The **Provider** exposes the *dataContext* bindings to the *slot*, which represents any nested child.
+If the component needs Budibase APIs, use the SDK context exposed by the plugin runtime.
-**Result**
+Keep the top-level wrapper on the component and include the `use:styleable={$component.styles}` attribute so Budibase can apply builder styles correctly.
-
+## Work with context
-
+Context tells Budibase which bindings the component provides.
-
+Use it when your component needs to:
-
+* Expose static values
+* Expose schema-driven bindings
+* Register data from a datasource or data provider
-## Example: Data provider
+The context definition is a contract with the builder. If the component says it provides a binding, it must actually supply that binding at runtime.
-Whilst you can query data from an API within your custom component, it can be better practice to expose a [Data provider](https://docs.budibase.com/docs/data-provider) so that the user has flexibility to choose their own [Datasource](https://docs.budibase.com/docs/data-sources).
+Keep the context definition as small as possible and document only the bindings the component actually provides.
-### Update the schema
+## Configure the schema
-Include the following setting in your schema:
+`schema.json` defines:
-```json schema.json
-"settings": [
- {
- "type": "dataProvider",
- "label": "Data provider",
- "key": "dataProvider",
- "required": true
- }
-]
-```
-
-Which will appear as follows in Budibase:
+* The settings shown in the builder
+* The type of data the component accepts
+* The outputs exposed to other components
-
+If your component is used inside forms, define the field and validation settings explicitly.
-
+The schema also controls whether the component can hold children, whether the settings bar is shown, and which styling controls are available.
-### Using the data provider
+## Register form fields
-The data provider has a *rows* property which contains an array of objects for the current page of results.
+For form-field components, register the field with the parent form context.
-In this example we will look at a [Spotify playlist component](https://github.com/YuanZhang98/budibase-spotify-playlist).
+Use the form API to connect the custom component to the form value, field type, and validation rules.
-Add the following code to the *script* tag of your component:
-
-```javascript Component.svelte
-export let dataProvider
-$: playlists = dataProvider?.rows ?? [];
-```
+If the component is not a form field, you do not need to register it with the form context.
- The first line assigns the data provider from the settings. Next we get all the rows and default to empty if needed.
+## TypeScript support
-We can now use the playlists variable to loop over each to be displayed in our component:
+The template uses JavaScript by default.
-
+If you want TypeScript, add it after the component works in JavaScript and update the project setup accordingly.
-
+The migration path is incremental:
-
+1. Make the JavaScript version work.
+2. Add TypeScript support.
+3. Move the entry file to `index.ts` if needed.
+4. Keep the component API unchanged while you change the implementation language.
-## Example: Event handler
+## Testing
-As seen in the component [Actions](doc:actions) it is possible for components to have *On Click*, *On Change* or any other event!
+Use `test/index.spec.ts` to verify the component renders and exposes the expected behaviour.
-In this example, we will look at a [timer component](https://github.com/doggodoge/bb-timer) that will trigger some user specified action when the count down goes to zero.
+Focus on:
-### Update the schema
-
-Include the following setting in your schema:
-
-```json schema.json
-"settings": [
- {
- "type": "event",
- "key": "timeout",
- "label": "On Timeout"
- }
-]
-```
+* Required props
+* Context registration
+* Form integration
+* Validation behaviour
-This will appear in the Budibase builder like so:
+For components that expose builder settings, also test that the schema and runtime props stay in sync.
-
+## Example usage
-### Wiring the event
+The star rating example in this section is most useful as a pattern for form components:
-The user is now able to trigger any sequence of Budibase actions upon the *On Timeout* event. We now need to determine when to fire this event.
-
-To do this we will need to import the Event Dispatcher from Svelte and then fire our event. This can be done like so:
-
-```typescript Timer.svelte
-import { createEventDispatcher } from 'svelte';
-const dispatch = createEventDispatcher();
-
-...
-
-if (counter < 1) {
- dispatch('timeout');
-}
-```
-
-Then in the parent component, we take a prop which matches the key name **timeout**, and also include the **on** listener like so:
-
-```html Component.svelte
-
-
-
-
-
-```
-
-### On change event
-
-An *On Change* event will be similar to the above, but you also will want to provide context to expose the `{{ Field Value }}` binding.
-
-To do so, add the following to your **schema.json**:
-
-```json schema.json
-"settings": [
- {
- "type": "event",
- "label": "On Change",
- "key": "onChange",
- "context": [
- {
- "label": "Field Value",
- "key": "value"
- }
- ]
- }
-]
-```
-
-Then you can use your *onChange* function similar to the following:
-
-```html Component.svelte
-
-
-
-
-
-```
+* add a field setting in `schema.json`
+* map that setting to a prop in `Component.svelte`
+* register the field with the parent form
+* apply validation only where the form needs it
-It is important to note that we are providing an object with the 'value' key, to make sure Budibase can detect the binding.
+Keep examples focused on one integration path. That makes the component easier to adapt to another use case later.
-
+## Related guides
-
+* [Component schema](/docs/component-schema)
+* [Budibase CLI setup](https://docs.budibase.com/docs/budibase-cli-setup)
+* [Custom plugin overview](https://docs.budibase.com/docs/custom-plugin)
diff --git a/docs/Develop & Scale/custom-plugin/custom-datasource.md b/docs/Develop & Scale/custom-plugin/custom-datasource.md
index 42f839730..c7f40eee8 100644
--- a/docs/Develop & Scale/custom-plugin/custom-datasource.md
+++ b/docs/Develop & Scale/custom-plugin/custom-datasource.md
@@ -10,361 +10,121 @@ metadata:
next:
description: ''
---
-
-
-
+Custom datasource plugins let you connect Budibase to external systems through read, create, update, and delete operations.
+Use them when your app needs data from a source that is not already covered by a built-in connector.
-## Purpose
+## Before you start
-Datasource plugins are simple in their nature, allowing you to write an extension to Budibase that defines external [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations that are useful to your business. Datasource plugins are data centric, focused on getting the data you need into Budibase so that you can build apps quickly and easily.
+Make sure you have:
-## Getting started
+* The latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup)
+* Node.js `v16` or later
+* A self-hosted Budibase installation
-Make sure you have the latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup) installed. Even if you have previously installed the CLI, you may want to install it again to be sure you have the latest version.
+Datasource plugins are not available in Budibase Cloud.
-Also make sure that you have **node v16+** installed. You can run `node --version` to see if you need to upgrade.
+## Create the plugin
-Please note that datasource plugins can only be used in self host installations - they cannot be imported to the Budibase Cloud.
-
-Once that's done, we need to initialise a template for our datasource.
-
-Navigate to the folder location you want to create your plugin, and execute the following command:
+1. Go to the folder where you want to create the plugin.
+2. Run:
```shell
budi plugins --init datasource
```
-Next you will receive a series of prompts to enter details about your datasource. You can press enter without providing a value if you are happy to use the placeholder.
-
-
-
-Within the newly created custom datasource project you should see a tree structure as follows:
+3. Complete the setup prompts.
+4. Open the generated project in your editor.
-```yaml
-src:
- datasource.ts: The code for your custom datasource. Do not rename.
+The template includes:
-test:
- index.spec.ts: A Jest test file that includes template unit tests for each CRUD method.
-
-package.json: The version, license and dependencies for your datasource.
+* `src/datasource.ts` for the datasource implementation
+* `test/index.spec.ts` for template tests
+* `schema.json` for builder configuration
+* `package.json` for package metadata and dependencies
-schema.json: The meta data and available builder settings for your datasource.
-```
-
-
+## Build and watch
-## Building your datasource
+Build the project with:
-After you have created your new datasource directory, execute the following:
-
-```shell Shel
+```shell
cd my-datasource
yarn build
```
-Assuming you have [updated the server env variable](https://docs.budibase.com/docs/custom-plugin#hot-reloading-developers-only) you should now be able to see your datasource template in Budibase:
-
-
-
-
-
-
-### datasource.ts
-
-This is where your code will live for your datasource. It is recommended that it is developed in TypeScript. The default implementation is a basic HTTP connector, but you can make use of a wide range of npm packages to build the desired integration.
-
-To make sure your saved changes are passed through to your Budibase server, you must execute a `yarn watch` within your custom datasource project. This will allow your local Budibase app builder to see those changes in real-time, which is very handy for getting your datasource to behave just right.
-
-As the file is TypeScript, it is recommended that you use the **import** keyword over **require**. E.g
-
-> 👍
->
-> ```javascript
-> import Surreal from "surrealdb.js"
-> ```
-
-> ❗
->
-> ```javascript
-> const Surreal = require("surrealdb.js");
-> ```
-
-#### Methods
-
-The `datasource.ts` file contains four CRUD methods which must be implemented.
-
-The signature of each method should match the definition provided by the [Schema](/docs/custom-datasource-schema).
-
-You will also notice a `request` method in the created template. This is not mandatory, however it can be useful to have a method that executes shared code between your CRUD methods.
-
-
-
-### Test
-
-The `index.spec.ts` test file includes template unit test methods for each CRUD method in your datasource. You can implement these to meet your own specification, however failing tests wont prevent the datasource from being built and used within the Budibase builder.
-
-
-
-### Schema
-
-On the next page we will discuss the intricacies of the datasource schema.json file - which defines the inputs and outputs of your datasource and its queries.
-
-You can jump to the datasource schema page [here](/docs/custom-datasource-schema).
-
-
-
-### Adding an icon
-
-By default your custom datasource will present with the following icon:
+During development, run `yarn watch` so Budibase picks up code changes without rebuilding manually.
-
+If you change `schema.json`, refresh the app builder to pick up the updated schema.
+## Implement the datasource
-It is nice to add an icon to your custom datasource so that it is distinguishable from other custom datasources. Simply add an **svg** file to the root of your project. It should be called `icon.svg`.
+`datasource.ts` is where the connector logic lives.
-
+Use it to:
+* Define the required CRUD methods
+* Share request logic through a helper method such as `request`
+* Add any provider-specific headers, auth, or request shaping
-
+The template includes four CRUD methods. Match the method signatures in the datasource schema so the builder can call them correctly.
-## Example: n8n workflow connector
+The default template is a basic HTTP connector. You can replace that logic with the client or SDK for your target system as long as the datasource methods still return the expected shapes.
-This tutorial will show you how to build an n8n connector that can perform CRUD operations on workflows.
+## Configure the schema
-This example will assume you have configured the server [plugins directory env var](https://docs.budibase.com/docs/custom-plugin#hot-reloading-developers-only)
+`schema.json` defines:
-### Step 1 - Initialise
+* Builder settings
+* Required credentials
+* Query inputs and outputs
+* The methods exposed by the plugin
-Initialise your custom datasource as follows:
+Start with the datasource schema docs, then add only the fields your connector actually needs.
-
+Keep the schema narrow:
-Next make sure the Budibase dev environment is running.
+* Put connection details in `datasource`
+* Put action-specific inputs in the relevant query definitions
+* Use `fields` only when you need explicit builder inputs
-Open the newly created custom datasource directory in your IDE, for example VSCode, and run `yarn watch`. Verify your plugin is available in the plugins list in the Budibase portal.
+The schema also controls the friendly name shown in the builder and the documentation link that appears with the plugin.
-
+## Add an icon
+Add an `icon.svg` file at the root of the project if you want a custom icon in the Budibase builder.
-
+## Testing
-### Step 2 - Schema updates
+`test/index.spec.ts` includes template tests for each CRUD method.
-Update the `schema.json` to include the **host** and **apiKey** settings.
+Use those tests to verify:
-
+* Requests are shaped correctly
+* Responses are handled consistently
+* Error cases fail in a predictable way
-You can also update the **docs** link and **friendlyName**.
+When you add a custom method beyond the default CRUD set, add a matching test for that method too.
-Next update the **read** property of the schema. This will determine the available fields available for reading our n8n workflows. It should look as follows:
+## Example implementation
-
+The n8n connector example in this section shows the same pattern used by many datasource plugins:
-You will need to refresh your Budibase builder webpage to pull in schema changes. To test things so far, if you add a new datasource it should look as follows:
-
-
-
-
-
-And the **Read** query:
-
-
-
-
-
-### Step 3- Read method implementation
-
-Whilst we have provided the interface to let Budibase know which fields to expect for the custom datasource read method, we still have to write the implementation.
-
-First lets replace the existing config properties with the new ones: **host** and **apiKey**. Replace all references to the old properties with the new ones throughout the file.
-
-
-
-Next update the `read` method as follows:
-
-```javascript
-async read(query: { id: number, active: string, tags: string, limit: number, cursor: string }) {
- const opts = {
- method: "GET",
- }
- if (query.id > -1) {
- return this.request(`${this.host}/api/v1/workflows/${query.id}`, opts)
- }
- let queryParams = ""
- if (query.active) {
- queryParams += `&active=${query.active}`
- }
- if (query.tags) {
- queryParams += `&tags=${query.tags}`
- }
- if (query.limit) {
- queryParams += `&limit=${query.limit}`
- }
- if (query.cursor) {
- queryParams += `&cursor=${query.cursor}`
- }
- return this.request(`${this.host}/api/v1/workflows?${queryParams}`, opts)
- }
-```
-
-If an ID is provided then get the data for that particular workflow, otherwise grab all workflows using the optional query params.
-
-Finally, the n8n API expects the **apiKey** in the header, so we can add that in the `request` method as all calls will need authenticated.
-
-
-
-The official n8n API workflow docs can be found [here](https://docs.n8n.io/api/api-reference/#tag/Workflow).
-
-**Get all workflows**
-
-
-
-A similar process can be followed to implement the Create, Update and Delete methods. Remember to update the schema as needed.
-
-
-
-### Step 4 - Adding an additional method
-
-So far we have demonstrated how to implement CRUD methods for a custom datasource, but what if we want to add additional methods? For example, the n8n workflows API includes CRUD operations, but it also has endpoints for activating/deactivating workflows.
-
-First add the additional method properties to the schema like so:
-
-
-
-Upon refreshing your local Budibase builder webpage (you may also need to navigate out of your app and back in), then you should see the changes:
-
-
-
-Next add another method to your `CustomIntegration` class as follows:
-
-```javascript
-async activate(query: { id: string, active: string }) {
- const opts = {
- method: "POST",
- }
- if (query.active === "true") {
- return this.request(`${this.host}/api/v1/workflows/${query.id}/activate`, opts)
- }
- if (query.active === "false") {
- return this.request(`${this.host}/api/v1/workflows/${query.id}/deactivate`, opts)
- }
- throw new Error("Active must be 'true' or 'false'")
- }
-```
-
-Depending on the state of the active flag, the `activate` or `deactivate` endpoint is called. An error is thrown if a boolean is not provided.
-
-You can view the finished datasource plugin, and also use the GitHub URL to import the plugin: [https://github.com/melohagan/budibase-datasource-n8n-workflow](https://github.com/melohagan/budibase-datasource-n8n-workflow)
-
-
-
-## Example: SQL query
-
-The previous example shows you how to include functions with _field_ type. You may want your custom datasource to support SQL queries with basic syntax highlighting.
-
-> 📘
->
-> This example is taken from the [SurrealDB custom datasource integration](https://github.com/melohagan/budibase-datasource-surrealdb)
-
-1. First update your schema to include a function that takes a _sql_ type function.
-
-
-
-2. Next include the `SqlQuery` type from Budibase types.
-
-
-
-3. Finally update your method signature to include the _query_ parameter. You can access the SQL string via `query.sql`
-
-
-
-**Result**
-
-
-
-
-
-## Example: Extra fields for handling modes
-
-So far we have looked at examples where we perform CRUD operations within a single domain/mode. In this example we will look at handling different modes for when you want to mostly share CRUD functionality, but make slightly different calls depending on the selected mode.
-
-> 📘
->
-> Specifically, we will be looking at handling CRUD operations for Subscriptions, Subscription Items and Subscription Schedules, all in one [datasource plugin](https://github.com/melohagan/budibase-datasource-stripe-subscriptions)
-
-Update the schema to include the `extra` property. In this case it was placed _after_ the `query` property:
-
-```json json
-"extra": {
- "type": {
- "displayName": "Type",
- "type": "list",
- "required": true,
- "data": {
- "create": [
- "Subscriptions",
- "Subscription Items",
- "Subscription Schedules"
- ],
- "read": [
- "Subscriptions",
- "Subscription Items",
- "Subscription Schedules"
- ],
- "update": [
- "Subscriptions",
- "Subscription Items",
- "Subscription Schedules"
- ],
- "delete": [
- "Subscriptions",
- "Subscription Items",
- "Subscription Schedules"
- ],
- "list": [
- "Subscriptions",
- "Subscription Items",
- "Subscription Schedules"
- ],
- "searchSubscriptions": [
- "Subscriptions"
- ],
- "releaseSchedule": [
- "Subscription Schedules"
- ]
- }
- }
- }
-```
+* initialize the plugin
+* add the required schema fields
+* implement the datasource methods
+* add any extra custom operations the API supports
-The lists indicate which options are available depending on which function has been selected. For example:
+Use that example as a guide for how to add connection fields, shape queries, and expose extra operations such as workflow activation.
-
+## Development notes
-
+* Datasource plugins are data centric
+* They are only available in self-hosted Budibase
+* `yarn watch` is required while iterating locally
+* Rebuild after changing project structure or dependencies
-Next we can use the _extra_ information to make a decision about which endpoint to hit. For example, the **create** method can be changed to the following:
+## Related guides
-```javascript
-async create(query: { json: object; extra: { [key: string]: string } }) {
- if (query.extra.type === "Subscriptions") {
- return await this.stripe.subscriptions.create(query.json as Stripe.SubscriptionCreateParams)
- }
- if (query.extra.type === "Subscription Items") {
- return await this.stripe.subscriptionItems.create(query.json as Stripe.SubscriptionItemCreateParams)
- }
- if (query.extra.type === "Subscription Schedules") {
- return await this.stripe.subscriptionSchedules.create(query.json as Stripe.SubscriptionScheduleCreateParams)
- }
- throw new Error("You must provide a type!")
- }
-```
\ No newline at end of file
+* [Datasource schema](/docs/custom-datasource-schema)
+* [Budibase CLI setup](https://docs.budibase.com/docs/budibase-cli-setup)
+* [Custom plugin overview](https://docs.budibase.com/docs/custom-plugin)
diff --git a/docs/Develop & Scale/custom-plugin/index.md b/docs/Develop & Scale/custom-plugin/index.md
index 198a8c63e..41204ff53 100644
--- a/docs/Develop & Scale/custom-plugin/index.md
+++ b/docs/Develop & Scale/custom-plugin/index.md
@@ -10,167 +10,82 @@ metadata:
next:
description: ''
---
-Budibase allows no-code users to build apps quickly, with more functionality available with a little bit of inline code. Despite this, not every conceivable scenario can be covered by default - but that's where custom plugins come in!
+Custom plugins extend Budibase when the built-in component and datasource options are not enough.
-Developers can write their own plugins for where custom datasources and components are needed. Some software development experience is required, however, the process for building plugins is designed to be streamlined and sleekly integrated into the Budibase platform.
+Use them to add:
-## Importing plugins
+* Custom components
+* Custom datasources
+* Custom automation steps
-An admin user can see a list of your plugins under the *Plugins* section of the Settings Modal. To get started using custom plugins, click "Add Plugin". Alternatively you can check our curated list of Budibase plugins, including data sources and components, contributed by the community.
+## When to use plugins
-
+Use a plugin when you need:
-You can import a plugin by pressing the `Add plugin` button. This will display a dialog box from which you choose a *Source*.
+* A UI component that is not built into Budibase
+* A datasource integration with custom CRUD behaviour
+* A custom automation action for backend logic
-
-
-
-
- Source name
-
+Keep the plugin surface area as small as possible. Plugins are easier to maintain when they do one thing well.
-
- Expected input
-
-
-
+## Install plugins
-
-
-
- URL
-
+Admin users can import plugins from the Settings modal.
-
- A URL path to a tarball, e.g. `.tar.gz` file.
+Supported import sources include:
- *Allows authentication headers to be added if needed.*
-
+After import, custom components appear in the builder component list and custom datasources appear in the datasource picker.
-
- The URL of an NPM package of a Budibase plugin
-
-
+Custom automation steps appear in the automation builder as selectable actions.
-
-
- Github
-
+## Develop locally
-
- The URL of a Github repository, that has a tarball asset in the latest release.
+For local development, Budibase needs a watched plugins directory.
- *Allows a Github Token to be added for accessing private repositories.*
-
-
+Use one of these setups:
-
-
- File Upload
-
+* Budibase CLI
+* Docker Compose
+* A local Budibase repository
-
- Allows an admin user to directly upload a plugin tarball.
-
-
-
-
+In each case, point Budibase at your plugins folder, then run `yarn watch` in the plugin project so changes are picked up automatically.
-Once your plugin has been imported, you will now be able to make use of it within all of your apps!
+The CLI path is usually the simplest option for plugin development because it gives you a local Budibase instance and a watched plugin folder in one flow.
-In the case of a **custom datasource**, it will appear as a new datasource at the bottom of the *Add datasource* dialog and can be used just like any other [data source](https://docs.budibase.com/docs/data-sources).
+## Limits and hosting
+* Custom datasources are self-host only
+* Custom components can also be used in cloud environments
+* Free plans have a plugin limit
-In the case of a **custom component**, it will appear in the builder components list under the *Plugins* section.
+Check the pricing page if you need more than the free tier allows.
-> 🚧 Cloud hosting
->
-> While custom components can also be imported in the cloud environment, custom datasources can only be used in a self-hosted environment, for security reasons.
+When you are developing locally, make sure the plugin directory path stays stable. Budibase watches that path for updates.
-
+## Update and delete plugins
-### Hot reloading (developers only)
+To update a plugin, import a new version with the same name.
-You must provide a path to your plugins folder so that Budibase knows where to import the plugins that you update while developing.
+To delete a plugin, select it from the plugin list and remove it from the settings UI.
-There are three main approaches to this, with the handiest being to run Budibase via the [CLI](https://docs.budibase.com/docs/budibase-cli-setup). Alternatively, you can run Budibase via [Docker compose](🔗) or a local repository.
+## Plugin types
-#### Budibase CLI
+Keep the plugin type aligned to the problem you are solving:
-Make sure you have installed the latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup), and initialize Budibase.
+* Use a component plugin for UI building blocks
+* Use a datasource plugin for CRUD access to external systems
+* Use an automation step plugin for backend actions
-If Budibase is already running, stop the service using the command:
+If the use case can be handled by a built-in component or action, prefer that first.
-`budi hosting --stop`
+## Related guides
-Add the plugins directory path using the command:
-
-`budi hosting --watch-plugin-dir /path-to-your-plugins-directory`
-
-Finally, run `budi hosting --start` and then do a `yarn watch` within your plugin repo.
-
-#### Docker compose
-
-Make sure you have installed the latest [Budibase CLI](https://docs.budibase.com/docs/budibase-cli-setup), and initialize Budibase.
-
-Add the plugins directory path (**case sensitive!**) to the **app-service** volumes of your docker compose file:
-
-```yaml Yaml
-services:
- app-service:
- volumes:
- - /Users//Documents/MyBudibasePlugins:/MyBudibasePlugins
-```
-
-Next, update your `.env` file to include the relative plugins directory, e.g. `PLUGINS_DIR=/MyBudibasePlugins`
-
-> 📘
->
-> The `.env` file will be hidden within the same folder as your `docker-compose.yaml`.\
-> Showing hidden files on [Windows](https://support.microsoft.com/en-gb/windows/show-hidden-files-0320fe58-0117-fd59-6851-9b7f9840fdb2).\
-> On macOS, press `Cmd+Shift+.` within the folder.
-
-Finally, run `budi hosting --start` and then do a `yarn watch` within your plugin repo.
-
-#### Budibase repo
-
-Assuming you have cloned the [Budibase repo](https://github.com/Budibase/budibase) and have it running locally, navigate to `packages/server/.env` and add the following variable:
-
-`PLUGINS_DIR=/Users//Documents/MyBudibasePlugins`
-
-Naturally, you should replace the value with the path to your plugins folder.
-
-If your server is already running, you will need to stop it and then start it with another `yarn dev` so that the new environment variable is loaded.
-
-You will also need to make sure you have run `yarn watch` in your plugin repo. See the custom component and datasource pages for more information on development.
-
-
-
-### Maximum number of plugins
-
-On the free plan you can add a maximum of 10 plugins. Upgrade to unlock *unlimited* plugins.
-
-A full list of pricing plans can be seen here: [https://budibase.com/pricing/](https://budibase.com/pricing/)
-
-
-
-## Updating a plugin
-
-To update an existing plugin, simply import a plugin with a matching name.
-
-Currently, the versioning must be handled manually, where you can have multiple folders for each version of your plugin.
-
-
-
-## Deleting a plugin
-
-Click on one of the plugins from the plugins list. This will pop up a dialog from which you can press `Delete`
-
-What’s Next\
-Tell your users what they should do after they've finished this page
+* [Custom component](https://docs.budibase.com/docs/custom-component)
+* [Custom datasource](https://docs.budibase.com/docs/custom-datasource)
+* [Custom automation step](https://docs.budibase.com/docs/custom-automations)
+* [Budibase CLI setup](https://docs.budibase.com/docs/budibase-cli-setup)
diff --git a/docs/Develop & Scale/custom-plugin/quickstart-plugins.md b/docs/Develop & Scale/custom-plugin/quickstart-plugins.md
index 765ad9c08..7f7986b5b 100644
--- a/docs/Develop & Scale/custom-plugin/quickstart-plugins.md
+++ b/docs/Develop & Scale/custom-plugin/quickstart-plugins.md
@@ -10,43 +10,34 @@ metadata:
next:
description: ''
---
-### Creating a Budibase Dev Environment Locally
+Use the plugin quickstart to spin up a local Budibase development environment for plugin work.
-The budibase CLI provides an all-in-one command for spinning up a local dev environment. This will spin up a fresh budibase server with a setup wizard that will allow you to develop plugins. Let's create a directory where we will store our budibase plugins. You can name this anything that you want, but we are going to call ours `budibase-plugins`.
+## Start the dev environment
-`mkdir budibase-plugins`
+1. Create a workspace folder for your plugins.
+2. Go to that folder.
+3. Run:
-`cd budibase-plugins`
-
-You can then use the budibase CLI command to start the dev environment.
-
-`budi plugins --dev`
-
-You will see the output below. Since we already navigated to the directory where we will create plugins, we can just hit enter on the `Directory to watch` step, since we are already in there. If you are running the `dev` command in another directory, you will need to point budibase to the directory where you plan to develop your plugins.
-
-```Text she
-? Directory to watch ./
-Starting services, this may take a moment - first time this may take a few minutes to download images.
-Recreating plugins_budibase_1 ...
-Recreating plugins_budibase_1 ... done
-Services started, please go to http://localhost:10000 for next steps.
-Configuration has been written to docker-compose.yaml
-Development environment started successfully - connect at: http://localhost:10000
-Use the following credentials to login:
-Email: admin@admin.com
-Password: admin
+```text
+budi plugins --dev
```
-Credentials to your dev instance will be printed to the console and you can then access your instance: `localhost:10000` and log in.
+4. Accept the default directory to watch if you are already inside the plugins folder.
+
+The CLI prints the local Budibase URL and login credentials when the environment starts.
-
+## Open the environment
-From here, you will be able to access all of your budibase plugins inside the `budibase-plugins` directory.
+1. Open the local Budibase URL shown in the CLI output.
+2. Log in with the printed credentials.
+3. Open the plugins area in the builder.
-
+From there, you can work on any plugin stored in the watched directory.
-To get started with creating a plugin or a datasource, follow the specific guides.
+## Next steps
-[Custom component](https://docs.budibase.com/docs/custom-component)
+Use the dedicated guides for the plugin type you want to build:
-[Custom datasource](https://docs.budibase.com/docs/custom-datasource)
+* [Custom component](https://docs.budibase.com/docs/custom-component)
+* [Custom datasource](https://docs.budibase.com/docs/custom-datasource)
+* [Custom automation step](https://docs.budibase.com/docs/custom-automations)
diff --git a/docs/Getting started/examples-and-guides/calculate-field-value-on-save.md b/docs/Getting started/examples-and-guides/calculate-field-value-on-save.md
index 2347c57fb..22fc45ae5 100644
--- a/docs/Getting started/examples-and-guides/calculate-field-value-on-save.md
+++ b/docs/Getting started/examples-and-guides/calculate-field-value-on-save.md
@@ -10,107 +10,32 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use this pattern when users enter values in one unit, but the table should always store a standard unit.
-{`
-
-`}
-
-## Scenario
-
-[Forms](doc:forms) will often have [number fields](https://docs.budibase.com/docs/text-inputs#number-field) for inputting measurements, which could be in different units such as ounces and kilograms, centimetres and inches, or even different currencies.
-
-This guide will demonstrate how to allow an End User \<\{`
-Challenge:
-★★★☆☆
-`}
-
-***
-
-## Steps
-
-1. In the Data section, create a new table using Budibase DB, name it Distance
-2.
-
- Add two columns to your events table - a text column called "event" and a number column called "distance"
-
-
-3. Go to the app screen, create a new app called Calculate field value on save
-4. Create a new blank screen (if you haven't already), and add a Form component (not to be confused with a Form Block). Set type to "create" and the schema to your events table.
-
-
-5. Add a **Field Group** component, and click "Update form fields" - this will fetch the columns from your table and create an input field for each. Set the "Type" option to 3 Columns.
-
-
-6. Add an Options Picker as a child of your form, and set the field and label to "unit". Set the Options Source to custom, and click "define options.
-
-
+## Build the form
- Add 3 options:
+1. Add a create form for the table
+2. Add an options picker for the unit field
+3. Use custom options for the supported units
- | Units | Value |
- | :----- | :---- |
- | Meters | 1 |
- | Yards | 0.914 |
- | Feet | 0.30 |
-7. Be sure to set the default value to 1, and uncheck "Alphabetical"
-8. Add an "On change" action to the options picker using the "Update Field Value" action\
- i. Select the *Form*\
- ii. Set *Type* to 'Set value'\
- iii. Select the *Distance* form field\
- iv. Click the lightning bolt and select the *JavaScript* tab. Add the following:
+## Calculate the value
- ```javascript JavaScript
- const previousUnit = $("New Form.Fields.units");
- const newUnit = $("Field Value");
- const previousDistance = $("New Form.Fields.distance")
+1. Add an `On change` action to the unit picker
+2. Read the previous unit and the new unit
+3. Recalculate the value with `Update Field Value`
- return (previousDistance * (previousUnit / newUnit)).toFixed(4);
- ```
+## Save the row
-> 📘 Previous and new values on change
->
-> It's worth bringing attention to the use of the options picker bindings.
->
-> `$("New Form.Fields.UnitPicker")` will get the value of the field before the change occurs.
->
-> `$("Field Value")` is the new value that will update the field when the *on change* finishes.
+1. Add a save button
+2. Save the form data
+3. Multiply the entered value by the selected unit factor before saving
+4. Clear the form after saving
-8. Add a "required" Validation setting to the options picker - the default error message is simply "Error", so be sure to add something useful like "Please specify units" to help the user understand the issue.
+## Why this works
-
+The user sees the unit they selected, but the table always stores one consistent value.
-9. As a child-component of your form, and a sibling of your field group, add a new button. Update the button text to "Save", and add an Icon "save-3-fill".
-
-
-
-10. Add an On Click action "Save Row" to save the information from your form into your table - (If you don't specify any columns, all fields will be saved by default). In our case we want to select the 'Distance column' and provide the following binding: `{{ multiply New Form.Fields.Distance Form.Fields.units }}` in order to save all distances in meters.
-
-
-
-
-
-11. Add another On Click action to validate your form
-
-
-
-12. It's also good practice to clear a form after submitting. Add another action "Clear Form" and chose your form to be reset.
-
-
-
-13. Add a table at the base-level of the component tree, as a sibling of your form component, and set it to fetch data from the events table. Initially it will be empty, but you can now publish your app and add new events using Meters, Feet or Inches.
-
-
-
-
-
-
-
-***
-
-## Workspace export
+## Workspace export
*Downloads may take a few seconds.*
@@ -122,19 +47,17 @@ This guide will demonstrate how to allow an End User \<\ Download
-`}
\ No newline at end of file
+`}
diff --git a/docs/Getting started/examples-and-guides/cascading-dropdown-filters.md b/docs/Getting started/examples-and-guides/cascading-dropdown-filters.md
index 04c3e8dab..a84cb3f22 100644
--- a/docs/Getting started/examples-and-guides/cascading-dropdown-filters.md
+++ b/docs/Getting started/examples-and-guides/cascading-dropdown-filters.md
@@ -10,83 +10,38 @@ metadata:
next:
description: ''
---
-## Try it out!
-
-{`
-
-`}
+Use cascading dropdown filters when one picker should limit the values in another.
## Scenario
-You have a **Country** table and a **City** table, with a relationship defined between them as *One Country -> Many Cities*.
-
-Your form will have an [Options picker](doc:option-picker) that will allow the end-user to select a country, which will then populate a second [Relationship picker](https://docs.budibase.com/docs/pickers#relationship-picker) with the cities that relate to the chosen country.
-
-{`
-Challenge:
-★★☆☆☆
-`}
-
-***
-
-## Steps
-
-1. `Add component`: **Form**
-2. In the Settings Panel, set *Schema* to 'Country'
-
-
+This guide uses a `Country` table and a related `City` table.
-3. `Add component`: **Data provider**
-4. In the Settings Panel, set *Data* to the 'Country' table
-5. Set the *Limit* to 200, and untick *Paginate*
+The country picker controls which cities appear in the city picker.
-
+## Build the form
-6. `Add component`: **Options Picker**
-7. In the Settings Panel, enter 'Country' for the *Field*, and add a *Label*
-8. Tick *Autocomplete*
-9. Set *Options source* to 'Data provider'
-10. Set *Label column* to 'country' and set *Value column* to 'country\_id'
+1. Add a [Form](doc:forms)
+2. Set the form schema to `Country`
+3. Add a [Data provider](doc:data-provider) for the country table
+4. Add an [Options picker](doc:option-picker) for the country field
+5. Add a [Relationship picker](doc:filter-by-relationships) for the cities field
-
+## Filter the cities
-11. `Add component`: **Relationship Picker** and select the 'Cities' from the *Field* dropdown
-12. Click the button beside *Filtering*
-13. `Add filter`:
- 1. 'country\_id' Equals Binding `{{ Country Form.Fields.Country }}`
- 2. Be sure to `Save`
+1. Open the relationship picker filters
+2. Match the city rows to the selected country
+3. Disable the city picker until a country is selected
+4. Clear the city value when the country changes
-
+## Why this matters
-14. Click on the `Conditions` tab and click `Configure conditions`
-15. `Add condition`:
- 1. 'Update Setting Disabled' TO \[✓] IF `{{ Country Form.Fields.Country }}` 'Is empty'
- 2. Be sure to `Save`
+This keeps the second picker limited to valid values and avoids stale selections.
-
+## Try it out
-> 👍 Layout
->
-> Use [Field group layouts](https://docs.budibase.com/docs/field-groups#layout) or [Containers](doc:container) to align the pickers as desired.
->
-> More on field layout in this recipe: [Display multiple fields in one row](doc:display-mutiple-fields-in-one-row)
-
-16. Finally we want to clear the selected cities when the country changes. Click on the **Country Options Picker**, and click the button beside the *On change* setting
-17. Add the **Update Field Value** action:
- 1. Select the *Form*
- 2. Set *Type* to 'Reset to default value'
- 3. Select the 'Cities' *Field* from the dropdown
- 4. Be sure to `Save`
-
-
-
-
-
-***
-
-## App export
-
-*Downloads may take a few seconds.*
+{`
+
+`}{`
@@ -96,19 +51,17 @@ Your form will have an [Options picker](doc:option-picker) that will allow the e
-`}
\ No newline at end of file
+`}
diff --git a/docs/Getting started/examples-and-guides/create-an-audit-table.md b/docs/Getting started/examples-and-guides/create-an-audit-table.md
index b45e2cbdb..35bbbf6d4 100644
--- a/docs/Getting started/examples-and-guides/create-an-audit-table.md
+++ b/docs/Getting started/examples-and-guides/create-an-audit-table.md
@@ -13,143 +13,33 @@ metadata:
next:
description: ''
---
-> 🚧 Heavy JavaScript usage
->
-> This guide uses a lot of JavaScript, which might be difficult to restructure if you have limited coding experience.
+Use this pattern when you need a basic audit trail for row updates.
-## Scenario
+When a user updates a row in `Patients`, an automation compares the old and new values and logs each change in `Patient audit`.
-This guide shows you how to track updates made to patient records by creating an audit log. When a user edits a row in the Patients table, an automation compares the previous and updated data, identifies which fields changed, and writes those changes to the Patient audit table.
+## Build the tables
-Use this to:
+Create `Patients` and `Patient audit`.
-* Maintain a history of edits for compliance or review
-* See which fields were changed, what the old value was, and what the new value is
+Store the field name, old value, new value, action type, related patient, and timestamp in the audit table.
-{`
-Challenge:
-★★★★☆
-`}
+## Build the screen
+
+Generate a table screen for `Patients`.
+
+This gives you the list screen plus the create and edit forms.
-## Steps
-
-1. Create the tables\
- In the Data area, create the following two tables:
-
- 1. Patients
- 1. First\_name - type text
- 2. Last\_name - type text
- 3. DOB - type date
- 4. Phone - type text
- 5. Email - type text
- 6. Address - type long form text
- 7. Last\_visit\_date - type date
- 8. Audit\_logs - relationship (one to many)
- 2. Patient audit
-
- 1. Column\_name (field that changed) - type text
- 2. Old\_value - type text
- 3. New\_value - type text
- 4. Action\_type (e.g. "UPDATE") - type single select
- 5. Patient - type relationship (many to one)
- 6. Changed\_at - type text and set the default value to `{{ now }}`
-
-
-2. Generate screens
-
- 1. Go to the Design area
- 2. Click the plus to add a new screen
- 3. Select Table
-
-
- 4. Select Patients as the source for this new screen
-
-
- 5. Then select New screen, this will generate the following;
-
-
-
- 1. A table component to view all patients with a create row button
- 2. A form page were you can create new patient records
- 3. A form page were you can edit existing records by clicking on a row in the table
-3. Set Up Automation\
- Navigate to the Automation area and create a new automation:
-
- 1. Click the `Updated by` trigger type for your automation
-
-
- 2. Select Patients as the source table it will watch for changes
- 3. Add a new step called JavaScript
-
-
- 4. Copy the below code into the automation step
-
- ```javascript
- const oldRow = $("trigger.oldRow");
- const newRow = $("trigger.row");
-
- const fieldsToCheck = [
- "First_name", "Last_name", "DOB",
- "Phone", "Email", "Address", "Last_visit_date"
- ];
-
- const changedFields = fieldsToCheck.reduce((changes, field) => {
- const oldVal = oldRow[field];
- const newVal = newRow[field];
- const normOld = normalize(oldVal);
- const normNew = normalize(newVal);
-
- if (!(normOld == null && normNew == null) && normOld !== normNew) {
- changes.push({
- Column_name: field,
- Old_value: oldVal == null ? "" : String(oldVal),
- New_value: newVal == null ? "" : String(newVal),
- Action_type: "UPDATE",
- _id: $("trigger.row")._id
- });
- }
- return changes;
- }, []);
-
- return changedFields;
-
- function normalize(v) {
- if (v == null) return null;
- if (v instanceof Date) return v.toISOString();
- if (typeof v === "string") {
- const d = Date.parse(v);
- return !isNaN(d) ? new Date(d).toISOString() : v.trim();
- }
- return v;
- }
-
- ```
-
- This returns an array of changed fields in the format expected by the audit table.
- 5. Add a Create row step with the source set to the Patient audit table
-
-
-
- 1. Add looping
- 2. Use the output value from the JavaScript step for the loop
- 3. Add the looped outputs against each column
- 1. | Columns | Binded values |
- | :----------- | :------------------------------------ |
- | Column\_name | `{{ loop.currentItem.Column_name }}` |
- | Old\_value | `{{ loop.currentItem.Old_value }}` |
- | New\_value | `{{ loop.currentItem.New_value }}` |
- | Action\_type | `{{ loop.currentItem.Old_value }}` |
- | Patient | `{{ loop.currentItem.\_id }}` |
-
-## Example
-
-**Automation Output**
+## Build the automation
-
+1. Trigger the automation when a `Patients` row is updated
+2. Add a JavaScript step to compare the old and new row values
+3. Return one item for each changed field
+4. Loop over the returned items
+5. Create one audit row per changed field
-**Audit logs in app**
+## Why this works
-
+Each update produces one audit row per changed field, so you can review the history later.
## App export
@@ -161,19 +51,17 @@ Use this to:
`}
diff --git a/docs/Getting started/examples-and-guides/filter-table-block-with-options-picker.md b/docs/Getting started/examples-and-guides/filter-table-block-with-options-picker.md
index 9ad86a01a..9c6bb509d 100644
--- a/docs/Getting started/examples-and-guides/filter-table-block-with-options-picker.md
+++ b/docs/Getting started/examples-and-guides/filter-table-block-with-options-picker.md
@@ -1,5 +1,5 @@
---
-title: Filter table with options picker
+title: Filter a table with an options picker
excerpt: Filter by region dropdown or country name
deprecated: false
hidden: false
@@ -10,66 +10,31 @@ metadata:
next:
description: ''
---
-## Try it out!
-
-{`
-
-`}
+Use an options picker when the table should filter from a fixed list instead of free-text search.
## Scenario
-The Table component allows you to use the head of each column as a search field, however there are cases were you may want to use different type of field for searching, such as an [Options picker](doc:option-picker).
-
-This guide will show you how to add search that will display results for a selected *Region* from a preset list of values, or countries matching a *Country* field.
-
-{`
-Challenge:
-★★☆☆☆
-`}
-
-***
+This guide filters a sales table by region.
-## Steps
+You can adapt the same pattern for any field with a fixed set of values.
-1. Download the following .csv file containing the sample data for this guide [Sample Data](https://drive.google.com/file/d/1Pl8WQ14L4_7jVzD4UgHLMc032TfKlK0y/view?usp=sharing)
-2. In the Data tab of your Budibase workspace, click "Create new table", and select the "Create a Table from a CSV or JSON file" to upload the sample data. You may need to reject columns that have been unsuccessfully imported, and choose a display column.
+## Build the screen
- 1.
-3. You may need to reject columns that were unsuccessfully imported, and select a Display Column.
+1. Add a [Table](doc:table) for the sales data
+2. Add a [Form](doc:forms)
+3. Add an [Options picker](doc:option-picker) inside the form
+4. Set the picker field and label to `Region`
+5. Use custom options for the allowed values
- 
-4. When the data has been successfully imported, you should see a table of sample sales data populated in the Data Tab.
-5. Create a new app
-6. Create a new blank screen. Add a new Table component, and set the Data option to Sales.
+## Add the filter
-
-7. Next, add a Form component\* and add an Options Picker as a child of the form.\
- \*Form components are not to be confused with Form Blocks
-8. In the settings of the Options Picker, you'll need to add a field value, and you should add a label - we'll set both to "Region".
-9. Set the Options Source to Custom.
+1. Open the table settings
+2. Add a filter on the region column
+3. Bind the filter value to the options picker field
- 
-10. Click Define options: `Add Option` for each region:
+## Multi-select variant
- 1. Australia and Oceania
- 2. Central America and the Caribbean
- 3. Middle East and North Africa
- 4. Sub-Saharan Africa
- 5. Asia
- 6. North America
- 7. Europe
-11. Head back to the Table settings, and add a filter.
-
- 
-12. Once the filter is set, you'll be able to select a region from the dropdown from within the builder, but to fully test you should click the preview button to take a closer look.
-
-You may also decide that you want to filter by multiple region, for example when looking for sales in Europe and Asia - In this case, you should use a Multi-Select Picker component, and your filter should use the "Is In" operator.
-
-
-
-
-
-***
+Use a [Multi-select picker](doc:multi-select-picker) and an `Is In` filter when users should choose more than one region.
## App export
@@ -83,19 +48,17 @@ You may also decide that you want to filter by multiple region, for example when
-`}
\ No newline at end of file
+`}
diff --git a/docs/Getting started/examples-and-guides/index.md b/docs/Getting started/examples-and-guides/index.md
index e26090222..e6e4c053c 100644
--- a/docs/Getting started/examples-and-guides/index.md
+++ b/docs/Getting started/examples-and-guides/index.md
@@ -10,58 +10,25 @@ metadata:
next:
description: ''
---
-To help you adopt Budibase quicker and uncover what's possible, we've dedicated this documentation section to guides, examples, templates, and more.
+This section collects step-by-step guides, example apps, templates, and reference material.
## Guides
-Within this section you will find a list of step-by-step tutorials for building common workflows within Budibase.
+The guides in this section follow a common pattern:
-Each follows a common consistent layout as follows:
-
-| Section | Description |
-| :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Try it out! | Displays an interactive [Embedded Budibase app](doc:embedded-app) so that you can quickly see if this use-case is relevant to the problem you are trying to solve |
-| Scenario | A description of what the recipe will entail, the problem being solved, and a star guide as a rough estimate to the challenge involved in implementing |
-| Steps | Direct steps including screenshots. Most guides will assume your [Datasource](doc:data-sources) has been configured |
-| App export | A download link for the [App export](https://docs.budibase.com/docs/export-and-import-apps) so that you can explore the recipe further |
+* `Try it out!` shows an embedded example app
+* `Scenario` explains the problem the guide solves
+* `Steps` walks through the build
+* `App export` provides a downloadable example
## Blogs and discussions
-If you can't find the recipe you are looking for, there are a number of blogs and Github discussions you can explore.
-
-| Title | Guide |
-| :----------------------------------------------------- | :----------------------------------------------------------------------------------------------- |
-| How to build a CRUD app with Budibase and Next.js | [Blog](https://budibase.com/blog/building-a-crud-app-with-budibase-and-next.js/) |
-| Creating related rows in more than one table | [Github answer](https://github.com/Budibase/budibase/discussions/4998) |
-| Access child form fields data from the parent | [Github answer](https://github.com/Budibase/budibase/discussions/5119) |
-| Add a linking/lookup table | [Github answer](https://github.com/Budibase/budibase/discussions/5299) |
-| Appending a relationship | [Github answer](https://github.com/Budibase/budibase/discussions/7888#discussioncomment-3760973) |
-| Navigating to detail screen for multiple selected rows | [Github answer](https://github.com/Budibase/budibase/discussions/8759#discussioncomment-4207094) |
-| Filtering related data on selected rows | [Github answer](https://github.com/Budibase/budibase/discussions/7419) |
-| Verify row is unique before saving | [Github answer](https://github.com/Budibase/budibase/discussions/6294#discussioncomment-2938345) |
-| Edit form fields inline | [Github answer](https://github.com/Budibase/budibase/discussions/9536) |
-| Conditional table cell - setting text color | [Github answer](https://github.com/Budibase/budibase/discussions/10204) |
-| How to build a public form | [Blog](https://budibase.com/blog/public-forms-how-to-build-them-in-minutes/) |
-
-## Build an internal app in 15 minutes
-
-[@Jamesqquick](https://twitter.com/jamesqquick)
-
-
+Use these when you want a worked example or a community answer that does not fit a guide.
## Templates
-Explore many [Budibase templates](https://budibase.com/templates/). Templates are free, 100% customizable, and take a couple of clicks to install. Find 6 of our favorite templates below:
+Use templates when you want a starting point you can install and adapt quickly.
-{`
-
-
- Create new row\
- Save current row (while editing a row)
-
-
-
-
-
- ESCAPE
-
-
-
- Cancels edit
-
-
-
-
-
- ⇥ (TAB)
-
-
-
- Shift focus one cell to the right
-
-
-
-
-
- SHIFT + Click
-
-
-
- With a cell selected, SHIFT + CLICK will select all cells between
-
-
-
-
-
- SHIFT + ↑↓←→
-
-
-
- With a cell selected, SHIFT + ↑↓←→ will select another row or column depending on which arrow pressed
-
-
-
-
-
-
-
-## Component Tree Shortcuts
-
-
-
-
-
- Key
-
-
-
- Function
-
-
-
-
-
-
-
- ⌘ + ↑
- CTRL + ↑
-
-
-
- Move the component up
-
-
-
-
-
- ↑
-
-
-
- Select the next component above
-
-
-
-
-
- ⌘ + ↓\
- CTRL + ↓
-
-
-
- Move the component down
-
-
-
-
-
- ↓
-
-
-
- Select the next component below
-
-
-
-
-
- ⌘ + D\
- CTRL + D
-
-
-
- Duplicate the component (and it's children)
-
-
-
-
-
- ⌘ + X\
- CTRL + X
-
-
-
- Cut the component (and it's children)
-
-
-
-
-
- ⌘ + C\
- CTRL + C
-
-
-
- Copy the component (and it's children)
-
-
-
-
-
- ⌘ + V\
- CTRL + V
-
-
-
- Paste from the clipboard (clipboard must hold a component, component must be valid child-component for the location)
-
-
-
-
-
- ⌘ + ←\
- CTRL + ←
-
-
-
- Collapse all nested components
-
-
-
-
-
- ←
-
-
-
- Collapse selected component
-
-
-
-
-
- ⌘ + →\
- CTRL + →
-
-
-
- Expand all nested components
-
-
-
-
-
- →
-
-
-
- Expand selected component
-
-
-
-
\ No newline at end of file
+Use keyboard shortcuts to move faster in the builder and data views.
+
+## General
+
+| Key | Action |
+| --- | --- |
+| `Escape` | Close modal |
+
+## Data
+
+| Key | Action |
+| --- | --- |
+| `Cmd + Enter` / `Ctrl + Enter` | Create a new row or save the current row |
+| `Escape` | Cancel editing |
+| `Tab` | Move one cell to the right |
+| `Shift + Click` | Select a range of cells |
+| `Shift + Arrow keys` | Extend the current selection |
+
+## Component tree
+
+| Key | Action |
+| --- | --- |
+| `Cmd + Up` / `Ctrl + Up` | Move component up |
+| `Up` | Select the component above |
+| `Cmd + Down` / `Ctrl + Down` | Move component down |
+| `Down` | Select the component below |
+| `Cmd + D` / `Ctrl + D` | Duplicate component |
+| `Cmd + X` / `Ctrl + X` | Cut component |
+| `Cmd + C` / `Ctrl + C` | Copy component |
+| `Cmd + V` / `Ctrl + V` | Paste component |
+| `Cmd + Left` / `Ctrl + Left` | Collapse all nested components |
+| `Left` | Collapse selected component |
+| `Cmd + Right` / `Ctrl + Right` | Expand selected component |
+| `Right` | Expand selected component |
diff --git a/docs/Getting started/examples-and-guides/lookup-record.md b/docs/Getting started/examples-and-guides/lookup-record.md
index c6391b9e7..ff91c4ecd 100644
--- a/docs/Getting started/examples-and-guides/lookup-record.md
+++ b/docs/Getting started/examples-and-guides/lookup-record.md
@@ -10,97 +10,39 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use a lookup query when a form needs to check whether a record exists before the user continues.
-{`
-
-`}
-
-*Note: The email addresses are displayed for demo purposes, so that you can test the lookup.*
-
-## Scenario
-
-In some [Forms](doc:forms), it is important to verify that the entered details are correct before proceeding. For example, you may want to check that an email address matches against a policy through a [REST API](doc:rest) call, or [SQL query](https://docs.budibase.com/docs/data-sources#custom-queries).
-
-{`
-Challenge:
-★★★☆☆
-`}
-
-***
-
-## Steps
-
-1. In the **Data** section, click on your external Datasource, and click on the `Queries` tab. Click `Create new query`.
-
-
-
-2. `Add binding`: 'email' and provide a valid default value
-3. Enter your lookup query, e.g. `select * from public."Employees" where "Email" = '{{email}}'`
-4. Click `Run Query` and then `Save Query`.
-5. Clear the default value of the binding - it is no longer needed now that we have our schema saved. Make sure to `Save Query` again.
+## Build the query
-
+1. Open the relevant datasource
+2. Create a query that looks up the row you need
+3. Add a binding for the lookup value
+4. Save and test the query
-6. Navigate to the **Design** section, `Add component`: **Form**
-7. `Add component`: **Field Group**, and set *Type* to 'Main with Sidebar'
-8. `Add component`: **Text Field**, and enter a *Field* name and *Label*
-9. `Add component`: **Icon**, and set the *Icon*, *Size* and *Color*
+## Build the form
-
+1. Add a [Form](doc:forms)
+2. Add the input field you want to check
+3. Add a button to run the lookup query
-10. Click the button beside *On click*
-11. `Add Action`: **Execute Query**
- 1. Select the *Datasource*
- 2. Select the *Query*
- 3. Check *Do not display default notification*
- 4. Provide the email binding, e.g. `{{ Search Form.Fields.Search Email }}`
+## Add the actions
-
+1. Run the lookup query
+2. Save the returned row ID to app state
+3. Stop the action chain when the query returns no rows
+4. Show a warning notification when the record is not found
-12. `Add Action`: **Update State**
- 1. *Set value* 'EmployeeID' to JavaScript binding:
- 2. ```javascript JavaScript
- return $("Action 1.Query result")?.data?.[0]?.["EMPLOYEE ID"]
- ```
-13. `Add Action`: **Continue if / Stop if**
- 1.
+## Show the result
-
+1. Add a [Repeater](doc:repeater) or [Form block](doc:form-block)
+2. Filter the data by the saved row ID
+3. Hide the component when no record was found
-14. `Add Action`: **Show Notification**
- 1. Set *Type* to 'Warning'
- 2. Set *Message* to 'Employee not found!'
- 3. Check *Auto dismiss*
-15. Be sure to `Save`
+## Try it out
-
-
-16. Click on the **Screen** component (root level of the Component Tree). `Add component`: **Repeater Block**
-17. In the Settings Panel, select the 'Employees' table, set *Limit* to 1, and click the button beside *Filtering*
-18. `Add filter`: 'EMPLOYEE ID' *Equals Binding* `{{ State.EmployeeID }}` and `Save`
-
-
-
-19. Click on the `Conditions` tab, and click `Configure conditions`
-20. `Add condition`:
- 1. *Hide component* IF `{{ State.EmployeeID }}` *Is empty*
- 2. `Save`
-
-
-
-21. `Add component`: **Form**, and set *Type* to 'Update', and the *Schema* to the 'Employees' table
-22. `Add component`: **Field group** and click `Update form fields`
-
-
-
-
-
-***
-
-## App export
-
-*Downloads may take a few seconds.*
+{`
+
+`}{`
@@ -110,19 +52,17 @@ In some [Forms](doc:forms), it is important to verify that the entered details a
`}
diff --git a/docs/Getting started/examples-and-guides/notify-user-if-rest-query-fails.md b/docs/Getting started/examples-and-guides/notify-user-if-rest-query-fails.md
index 05c75d7c2..da9c75332 100644
--- a/docs/Getting started/examples-and-guides/notify-user-if-rest-query-fails.md
+++ b/docs/Getting started/examples-and-guides/notify-user-if-rest-query-fails.md
@@ -10,88 +10,32 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use this pattern when a REST query should return success or failure immediately so the UI can react.
-{`
-
-`}
+## Build the automation
+
+1. Create an automation triggered by an app action
+2. Add the query fields you need
+3. Run the REST query
+4. Collect the query response so it returns to the UI
-## Scenario
+## Handle the response
-Typically when executing a REST query, such as creating a new resource with a POST request, the response will come back successfully and the End User can continue with their workflow.
+1. Save the response code to app state
+2. Stop the action chain when the status is not `200`
+3. Add success actions such as refreshing a provider or closing a panel
+4. Show an error component when the response is not successful
-There will however be occasions when the request is badly formed, or there is a timeout or server error. Ideally these situations should be caught, and the user should be notified so that they can either try again, or fix their request.
+## Why this works
-To accomplish this, we will make use of [Synchronous automations 🔒](doc:synchronous-automations).
+The UI can branch immediately based on the status code returned by the query.
-For demonstration purposes, this guide will use a dummy query that will either return a 200 or a 404 in the response body.
+## Try it out
{`
-Challenge:
-★★☆☆☆
+
`}
-***
-
-## Steps
-
-1. Go to the [Automations](doc:automation-steps) section of your app, and `Add automation`
-2. In the *Create Automation* modal, enter a *Name*, and select `App Action` as the trigger. `Save`.
-3. `Add field` for each binding that you will need to pass into your query
- 1. In this example we will use a simple pass boolean to determine the 200 or 404 response.
-
-
-
-4. Click the `+` below the trigger step, select `External Data Connector` and `Save`
-5. Select your query from the *Query* dropdown, and pass in your trigger fields as the query Bindings, for example: `{{ trigger.fields.pass }}`
-
-
-
-
- Our query with 'resource' binding as seen in the Data section
-
-
-6. Add the final automation step: **Collect Data**, which will allow us to return the response data to the End User.
-7. Under *What to collect* add the following binding: `{{ literal steps.1.response }}`. The literal keyword is used to ensure an object is returned, and not a string.
-
-
-
-8. With the automation setup, we can now go to the Design section, and `Add component`: **Button**
-9. In the Settings Panel, click the button beside *On click* to open the actions drawer.
-10. `Add Action`: **Trigger Automation**
- 1. Select the *Automation* you created. You should see an info note: *This automation will run synchronously*
- 2. Pass in any [On demand](doc:app-action) fields
-
-
-
-11. `Add Action`: **Update State**
- 1. *Set value* 'ResponseCode' to `{{ Action 1.Automation Result.value[0].code }}` (this binding will depend on how your API returns data)
-
-
-
-12. `Add Action`: **Continue if / Stop if**
- 1. *Continue if* `{{ State.ResponseCode }}` *Equals* 200
-13. Add actions that you want for the continue case, such as **Refresh Data Provider** and **Close Side Panel**.
-14. Be sure to `Save`
-
-
-
-15. `Add component`: **Headline** for the error scenario
-16. Click on the `Conditions` tab: `Configure conditions`
-17. `Add Condition`:
- 1. *Show component* IF `{{ State.ResponseCode }}` *Like* 4
- 2. `Save`
-
-
-
-***
-
-
-
-## App export
-
-*Downloads may take a few seconds.*
-
{`
@@ -100,19 +44,17 @@ For demonstration purposes, this guide will use a dummy query that will either r
`}
diff --git a/docs/Getting started/examples-and-guides/passing-bindings-in-url-parameters.md b/docs/Getting started/examples-and-guides/passing-bindings-in-url-parameters.md
index ab90f3db5..9101c302c 100644
--- a/docs/Getting started/examples-and-guides/passing-bindings-in-url-parameters.md
+++ b/docs/Getting started/examples-and-guides/passing-bindings-in-url-parameters.md
@@ -14,65 +14,43 @@ next:
slug: url-parameters
title: URL variables
---
-# Try it out!
+Use URL parameters when a list screen needs to open a detail screen for a selected row.
-In this scenario, I will be using the sample data provided in the internal Budibase DB to make a list of staff, each element in the list can be clicked to navigate to a page that displays more detailed information about that particular staff member. Follow along by building your own version, or download the app-export at the end of the page to take a closer look.
+## Build the list screen
-Firstly, I'll add a `Data Provider`, and nest a `Repeater` component, and then add `Container` component inside the Repeater.
+1. Add a [Data provider](doc:data-provider)
+2. Add a [Repeater](doc:repeater) inside the provider
+3. Nest a Container inside the repeater
+4. Use bindings from the repeater row to show the values you want
-
+## Build the detail screen
-
+1. Create a screen at a path such as `/staff/:id`
+2. Add a [Data provider](doc:data-provider) for the same table
+3. Filter the provider by `_id`
+4. Set empty filter behavior to return no rows
+5. Disable pagination
+6. Use `{{ New Data Provider.Rows.0 }}` to read the returned row
-In the Data Provider settings, I'll select the *Employees* table as the *Data* source. Now, any components I add inside the `Repeater` will have access to the bindings that reflect the *Employees* table.
+## Navigate to the detail screen
-I can set this by going to `Bindings >> New Repeater` and see the column names offered as bindings. I've used a `Headline` component to show *First Name* and *Last name*, and another `Headline` component to show *Employee Level*. With a little bit of styling we can start to see this screen coming together.
+Add an `On Click` action to the list item or row container.
-
+Use `Navigate To` and pass the row `_id` in the URL, for example:
-Next, I'll create a new screen that will become the detailed view of each employee. I'll set the URL of the screen to be `/staff/:id`. Pay close attention to the colon `:` - this is how we will denote a variable in a URL.
+`/staff/{{ New Repeater.Employees._id }}`
-
+## Recommended settings
-On my new screen, I'll add a `Data Provider` to get information from the *Employees* table, and set a filter for the `_id` from the *Employees* table to match the binding passed in the URL.
+Use `_id` rather than a human-readable field such as email.
-If a user accidentally managed to navigate to the `/staff/:id` screen without a valid `_id` being set, the default setting of "When filter empty: return all rows" would get every row in the table, and display the bindings of the first row it found. It's much better to catch that by returning no rows. I'll also disable pagination on the `Data Provider`.
+That keeps the URL stable and avoids guessable values.
-
+## Try it out
-Now inside my `Data Provider` I can add components that have access to my Data Provider in the bindings. It's important here to note that the `Data Provider` fetches rows from a table, which are returned as an array of objects, where each object is one row from the table. We will only ever get one row using these filters, but it will still be returned as an array. All that is to say that when digging into the bindings we should index the result of the Data Provider rows. Using `{{ New Data Provider.Rows.0 }}` will return the entire row as an object, and we can prop-drill one level further by adding a column name.
-
-
-
-
-
-I've applied some details to the page, referencing the individual row, and it's now starting to take shape. The last part of this task is the most important. Head back to the `/staff` screen, and on the `Container` component in the `Repeater component`, add an *On Click* action. Set a *Navigate To* action, and open the bindings drawer. In this example the destination will be `/staff/{{ New Repeater.Employees._id }} `, which puts the clicked container's `_id` into the URL, and passes it through to the `/staff/:id` screen.
-
-
- Click the lightning bolt to open the Bindings Drawer...
-
-
-Now, navigating from `/staff` will actually substitute `:id` for a row id, which the `Data Provider` uses to filter the table and present the information.
-
-## A few things to consider...
-
-Think carefully about what you pass into the URL. In this example we passed `_id`. We could have used something more human-readable like email, but that would mean people could manipulate the URL to include an email that they shouldn't have access to, or a sequential number like EMPLOYEE ID. Imagine the URL had been `/staff/:email`, and On Click we navigate to `/staff/{{ New Data Provider.Employees.Email }}` but we only listed staff who were not managers, it would be easy to circumnavigate that by just editing the URL in the browser to an email address of a manager.
-
-
-
-
-
-
-
-You might want to remove the path to `/staff/:id` from the navigation bar, otherwise people will be able to visit the page without setting an `_id`
-
-
-
-When working in the builder on the `/staff/:id` screen, no `_id` will have been set or passed, so it can be tricky to know that your variables have been set properly. In this instance, in your `Data Provider`, temporarily set "When filter empty" to "return all table rows", so that you get some values to work with. Make sure to return it to "return no rows" when you're finished.
-
-
-
-Try it for yourself, and click below to download the app used to create this guide.
+{`
+
+`}{`
@@ -82,19 +60,17 @@ Try it for yourself, and click below to download the app used to create this gui
`}
diff --git a/docs/Getting started/examples-and-guides/populate-form-fields-on-select.md b/docs/Getting started/examples-and-guides/populate-form-fields-on-select.md
index a50219f24..39994fa8a 100644
--- a/docs/Getting started/examples-and-guides/populate-form-fields-on-select.md
+++ b/docs/Getting started/examples-and-guides/populate-form-fields-on-select.md
@@ -10,66 +10,35 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use this pattern when a picker should load a selected record into a form.
-{`
-
-`}
-
-## Scenario
-
-Have a [Form block](doc:form-block) that displays some fields for a table record, you want the End User to be able to select a record from an [Options picker](doc:option-picker) and update the form fields with the correct information.
-
-[App state](doc:app-state) will be used to allow communication between our search field, and the details form.
-
-{`
-Challenge:
-★★☆☆☆
-`}
-
-***
-
-## Steps
+The picker stores the selected row ID in app state, and the form reads that value back.
-1. `Add component`: **Form**
-2. `Add component`: **Data Provider**
-3. In the Settings Panel, select the 'Employees' table
-4. Untick *Paginate*
+## Build the picker
-
+1. Add a [Form](doc:forms)
+2. Add a [Data provider](doc:data-provider)
+3. Add an [Options picker](doc:option-picker)
+4. Set the picker to use the data provider
+5. Set the picker value to the row `_id`
+6. Save the selected `_id` into app state on change
-5. `Add component`: **Options Picker**
-6. In the Settings Panel, type in a *Field* name and *Label*
-7. Check *Autocomplete*
-8. Select 'Data provider' as the *Options source*
-9. Select 'Email' as the *Label column* and '\_id' as the *Value column*
+## Build the detail form
-
+1. Add a [Form block](doc:form-block)
+2. Set the form type to `View`
+3. Point it at the same table
+4. Bind the Row ID to the saved state value
-10. In the **Data** section, right-click the 'Employees' row you want to be the Default Value. Click `Copy row _id`.
+## Why this works
-
+The picker chooses the record and the form reads the matching row directly from the table.
-11. Back to the **Design** section, paste the row **\_id** you copied into the *Default value* setting of the **Options Picker**
-12. Click the button beside *On change*. `Add Action`: **Update State**
- 1. *Set value* 'SelectedEmployee' to `{{ Field Value }}`
- 2. Be sure to `Save`
+## Try it out
-
-
-13. Click on the **Screen** component (root level of the Component Tree). `Add component`: **Form Block**
-14. In the Settings Panel, set *Type* to 'View', and *Table* to 'Employees'
-15. Set *Row ID* to `{{ State.SelectedEmployee }}`
-
-
-
-
-
-***
-
-## App export
-
-*Downloads may take a few seconds.*
+{`
+
+`}{`
@@ -79,19 +48,17 @@ Have a [Form block](doc:form-block) that displays some fields for a table record
`}
diff --git a/docs/Getting started/examples-and-guides/public-form.md b/docs/Getting started/examples-and-guides/public-form.md
index b0e295457..932eb4207 100644
--- a/docs/Getting started/examples-and-guides/public-form.md
+++ b/docs/Getting started/examples-and-guides/public-form.md
@@ -5,69 +5,42 @@ deprecated: false
hidden: false
metadata:
title: ''
- description: >-
- The document provides a step-by-step guide on creating a secure public form
- using Budibase 3.0, allowing data collection from unauthenticated users
- while ensuring only app admins can access the submitted data. It includes
- instructions on setting up tables, designing forms, configuring automations,
- and publishing the app.
+ description: ''
robots: index
next:
description: ''
---
-## Try it out!
-
-{`
-
-`}
-
-## Scenario
-
-This is for situations where you want to collect data from unauthenticated users, but want to keep your table secure so that only App admins can see the submitted data. With the release of Budibase 3.0, table RBAC has been streamlined, so we'll use automations to write this data securely to the table. It's ideal for public forms like surveys where a user submits data and doesn't ever have to see it again.
-
-{`
-Challenge:
-★★☆☆☆
-`}
-
-***
-
-## Steps
+Use this pattern when unauthenticated users should submit data, but only app admins should access the stored records.
-1. In the Data section, create a **new table** with columns for the data you want to capture. By default, only App admins can access this table, which is fine for our example.
+The public screen submits form data through an automation, which writes to the secure table.
-
-2. In the newly created app, create a **new screen** for your form
-3. Set the screen's **Access** to `Public user`
-4. Build a **Form** with field components that suit your table's schema.
+## Build the table
-
+Create the table that will store the submitted data.
-> ℹ️ Generating fields from the Form Schema
->
-> You can use your table as the Form Schema to generate the relevant field components in a Field Group, but please note that a public user won't have access to that schema, so they may see errors when using the app. Once you have created your form fields, switch the Form Schema to Custom to prevent any issues for public users.
+## Build the screen
-5. In the Automation section, create a **new automation** that is triggered by an *App Action*
+1. Create a new screen
+2. Set the screen access to `Public user`
+3. Add a form with fields that match the table schema
-
-6. **Change the role** for this automation to `Public user`
-7. **Add fields** that match the fields in your form
+## Build the automation
-
-8. **Add a Create Row step** to the automation
-9. Select the relevant table and use the relevant bindings from your automation trigger for each column value
+1. Create an automation triggered by an app action
+2. Set the automation role to `Public user`
+3. Add fields that match the form
+4. Add a create row step for the secure table
+5. Bind the trigger fields into the row columns
-
-10. Go back to your screen in the Data section and add a **Button component,** which we'll use to submit your form
-11. **Add a*Trigger Automation* On click action** to the Button that triggers the automation we made
-12. **Add bindings** from your Form fields to send them to the automation
+## Submit the form
-
-13. **Publish the app** and test the public form in a private window
+1. Add a button to the public screen
+2. Trigger the automation on click
+3. Pass the form field values into the automation
-
+## Why this works
-***
+The public user can only call the automation, not read the secure table directly.
## App export
@@ -81,19 +54,17 @@ This is for situations where you want to collect data from unauthenticated users
`}
diff --git a/docs/Getting started/examples-and-guides/save-two-linked-tables-in-one-form.md b/docs/Getting started/examples-and-guides/save-two-linked-tables-in-one-form.md
index 12fac1318..5872bcc79 100644
--- a/docs/Getting started/examples-and-guides/save-two-linked-tables-in-one-form.md
+++ b/docs/Getting started/examples-and-guides/save-two-linked-tables-in-one-form.md
@@ -1,5 +1,5 @@
---
-title: Link two entities in one form
+title: Save two linked tables in one form
excerpt: 'Example: Creating an employee record and linking them to a newly created job'
deprecated: false
hidden: false
@@ -10,111 +10,28 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use this pattern when one form needs to create a parent row and optionally create a related child row.
-{`
-
-`}
-
-## Scenario
-
-When creating a new job, you want the option of creating and linking a new employee to that job in the one form. For this example you should use the sample data supplied in the internal BudibaseDB tables.
-
-{`
-Challenge:
-★★★★☆
-`}
-
-***
-
-## Steps
-
-1. `Add component`: **Form Block**
-
-
-2. In the Settings Panel, select the 'Jobs' *Table*
-
- 
-3. At the very bottom of the settings panel, click `Eject` - this will break down the Form Block into it's constituent parts. We can see this in the component tree.
-4. `Add component`: **Options Picker**
-5. In the Settings Panel, enter 'ExistingEmployee' as the *Field*.
-
- 
-6.
-7. Set *Type* to 'Radio buttons'
-8. Set *Direction* to 'Horizontal'
-9. Set *Default value* to 'SELECT'
-10. Uncheck *Alphabetical*
-11. Set *Options source* to 'Custom'
-12. `Define options`:
- 1. | Label | Value |
- | :----------------------- | :----- |
- | Select existing employee | SELECT |
- | Create new employee | NEW |
+## Build the form
-
+1. Add a [Form block](doc:form-block)
+2. Set the table to `Jobs`
+3. Eject the form block
+4. Add an [Options picker](doc:option-picker) for choosing an existing or new employee
+5. Add conditions so the relationship field and the new employee fields only show when needed
-13. In the component tree, drag the **Relationshipfield** below the **Options Picker**
-14. Click on the `Conditions` tab
-15. `Configure conditions`: `Add condition`
- 1. Hide component IF `{{ Form.Fields.ExistingEmployee }}` Equals Binding NEW
- 2. `Save`
+## Save the rows
-
+1. Save the job first
+2. Save the employee second when `NEW` is selected
+3. Use the saved job ID to link the employee row to the new job
-16. `Add component`: **Container**
-17. Click on the `Conditions` tab
-18. `Configure conditions`: `Add condition`
- 1. Hide component IF `{{ Form.Fields.ExistingEmployee }}` Equals Binding SELECT
- 2. `Save`
+## Why this works
-
-
-19. `Add component`: **Headline**. Set *Text* to 'New Employee'
-20. `Add component`: **Field Group**
-
-
-21. Click on the **Form** and set the *Schema* to 'Employees'
-
-
-22. Click on the newly added **Field Group** and click `Update form fields`
-23. Set *Type* to `Two columns`
-24. [Delete](https://docs.budibase.com/docs/components#editing) the **Jobs** field
-
-
-
-25. You may also need to replace the *Text Field* for Address with a *Long Form Field* if it displays as an error.
-26. Click on the **Form** and set the *Schema* back to 'Jobs'
-
- 
-27. Click on the 'Save' **Button**, enter the settings using the cog-icon and `Define actions`
-28. Click the `X` beside each actions so that only the **Validate Form** and **Save Row** actions remain
-29. `Add Action`: **Navigate To** *Screen* '/jobs'
-30. `Add Action`: **Continue if / Stop if**
- 1. Continue if `{{ Form.Fields.ExistingEmployee }}` Equals NEW
-31. `Add Action`: **Save Row**
-
- 1. Set *Table* to 'Employees'
- 2. `Add Column` for each form field in the Employees **Field Group**. Set the *Value* to the form field bindings, e.g. `{{ Form.Fields.First Name }}`
- 3. `Add Column` for the 'Jobs' column. Set *Value* to `{{ Action 2.Saved row._id }}`. This will get the **\_id** of the Job that was saved in second action.
- 4. `Save` your actions!
-
- 
-
-> 📘 Employee validation
->
-> As the select form schema is for the *Jobs* table, you will need to add [custom validation rules](https://docs.budibase.com/docs/form-validation#custom-validation-rules) to the *Employee* fields you want validated.
->
-> The same is also true for options that are defined by the table schema, such as *Employee Level* in this case.
-
-
-
-***
+The first save creates the job row, and the second save can reference that new job ID.
## App export
-*Downloads may take a few seconds.*
-
{`
@@ -123,19 +40,17 @@ When creating a new job, you want the option of creating and linking a new emplo
`}
diff --git a/docs/Getting started/examples-and-guides/saving-in-progress-form.md b/docs/Getting started/examples-and-guides/saving-in-progress-form.md
index ef9cb5f74..3c7198b13 100644
--- a/docs/Getting started/examples-and-guides/saving-in-progress-form.md
+++ b/docs/Getting started/examples-and-guides/saving-in-progress-form.md
@@ -1,5 +1,5 @@
---
-title: Saving in progress form
+title: Save an in-progress form
excerpt: Allow your end users to pick up where they left off
deprecated: false
hidden: false
@@ -10,79 +10,32 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use this pattern when a multi-step form should remember the user's progress after refresh or return visits.
-> 👍
->
-> Refresh the page to see the saved form progress.
+Save both the current step and the current form values to app state.
-{`
-
-`}
-
-## Scenario
-
-This guide will assume you have a *Create* [Form](doc:forms) with a number of fields added across multiple [Form steps](doc:form-steps), with *back* and *next* buttons configured to navigate between the steps.
-
-If End Users close the page, and come back at a later point, you want them to be be able to pick up were they left off. Any field entries should be saved, as well as the active form step.
-
-This recipe will make use of the [Spectrum meter](https://docs.budibase.com/docs/form-steps#spectrum-meter) to act as a step counter, as well as [App state](doc:app-state) to cache the form progress.
-
-{`
-Challenge:
-★☆☆☆☆
-`}
-
-***
+## Build the form
-## Steps
+1. Add a create form
+2. Split it into multiple form steps
+3. Add back and next buttons for navigation
-1. For each of your **Back Buttons**, click on the *On click* button in the Settings Panel. `Add Action`: **Update State**
- 1. Set value 'FormStep' `{{ subtract New Employee Form.Current Step 1 }}`
- 2. Tick *Persist this value*
+## Save progress
-
+1. Update the step value in app state when the user moves between steps
+2. Persist the step value
+3. Save the current form values to a separate state key
+4. Persist the form values
-2. Add another **Update State** action
- 1. Set value 'FormValueStep1' `{{ literal New Employee Form.Value }}`
- 2. Tick *Persist this value*
- 3. Click `Save`
- > 📘 FormValue state
- >
- > Each form step will need its own *FormValue* state, so form step two should be called 'FormValueStep2', etc.
- >
- > Also note the use of the **literal** helper. This tells Budibase that the form value should be treated as an object, and not a string. When using JavaScript bindings, original types are kept.
+## Restore progress
-
+1. Bind the form's initial step to the saved step state
+2. Bind each field's default value to the saved form state
+3. Clear the saved state after submission
-3. Do the same for each of your **Next Buttons**, but the 'FormStep' binding in that case would be: `{{ add New Employee Form.Current Step 1 }}`
+## Why this works
-> 📘 When to save form progress
->
-> This approach will save the form value upon form step navigation, however you could do this [On Change](https://docs.budibase.com/docs/text-inputs#on-change) for more frequent saving, making sure to use the `{{ Field Value }}` binding.
-
-4. When the form is submitted, you will need to clear the in-progress state. Select your **Submit Button**, and click the actions button in the Settings Panel. `Add Action`: **Update State**
- 1. Set value 'FormStep' to 1
- 2. Tick *Persist this value*
-5. Add another **Update State** action for the number of form steps
- 1. Set value 'FormValueStep1' to *blank*
- 2. Tick *Persist this value*
- 3. Click `Save`
-
-
-
-6. Click on the **Form** component, and in the Settings Panel set *Initial form step* to `{{ State.FormStep }}`
-
-
-
-7. For each of your form fields, set the Default Value to `{{ State.FormValueStep1.[Field Name] }}`.
- 1. Note that the state binding will depend on the active step
-
-
-
-
-
-***
+The step state restores where the user was, and the form value state restores the entered values.
## App export
@@ -94,19 +47,17 @@ This recipe will make use of the [Spectrum meter](https://docs.budibase.com/docs
`}
diff --git a/docs/Getting started/examples-and-guides/scroll-to-form-field.md b/docs/Getting started/examples-and-guides/scroll-to-form-field.md
index fd008f84d..f23a6e1b0 100644
--- a/docs/Getting started/examples-and-guides/scroll-to-form-field.md
+++ b/docs/Getting started/examples-and-guides/scroll-to-form-field.md
@@ -1,5 +1,5 @@
---
-title: Scroll to top of screen
+title: Scroll to form field
excerpt: ''
deprecated: false
hidden: false
@@ -10,72 +10,34 @@ metadata:
next:
description: ''
---
-## Try it out!
-
-{`
-
-`}
+Use `Scroll To Field` when a form action should move the view to a specific field.
## Scenario
-The Scroll To action can be useful when using [Form steps](doc:form-steps), for example when clicking a button for the next step, you want to scroll to the top of the screen.
-
-{`
-Challenge:
-★★☆☆☆
-`}
-
-***
-
-## Steps
-
-1. `Add component`: **Form**
-2. In the Settings Panel set *Schema* to 'Jobs'
-
-
-
-3. `Add component`: **Form Step**
-4. `Add component`: **Headline**. Set *Text* to 'Job'
-5. `Add component`: **Field Group**
-6. In the Settings Panel click on `Update form fields`: `Update`
-
-
+Use this pattern when a multi-step form should move the user to the next section after changing steps.
-7. Click on the **Form** and set *Schema* to 'Expenses'
+## Build the form
-8. [Duplicate](https://docs.budibase.com/docs/components#editing) the **Form Step**
+1. Add a [Form](doc:forms)
+2. Set the form schema
+3. Add a `Form Step`
+4. Add a headline for each step
+5. Add the relevant `Field Group` components
-9. In the Settings Panel set *Step* to 2
+## Add the actions
-10. Change the **Headline** *Text* to 'Expenses'
+1. Add a button to the first step
+2. Add a `Change Form Step` action
+3. Add a `Scroll To Field` action
+4. Choose the target form and field
-11. Click on the **Field Group**: `Update form fields`: `Update`
+Repeat the same pattern for later steps if you want the view to keep following the form.
-12. `Add component`: **Button** and set *Text* to 'Back'
+## Try it out
-13. Set the *On click*: `actions`
-
-14. `Add Action`: **Change Form Step** - Select the *Form* and set *Step* to 'First step'
-
-15. `Add Action`: **Scroll To Field** - Select the *Form* and set *Field* to 'Quote Date'
-
-16. `Save`
-
-
-
-17. [Copy](https://docs.budibase.com/docs/components#editing) the **Button**
-18. Click on the **Field Group** in **Form Step 1**. Paste the **Button**
-19. In the Settings Panel set *Text* to 'Next'
-20. Under *On click*: `Define actions`
- 1. **Change Form Step** - Change *Step* to 'Next step'
- 2. **Scroll To Field** - Change *Field* to 'Expense Tags
- 3. `Save`
-
-
-
-
-
-***
+{`
+
+`}
## App export
@@ -89,19 +51,17 @@ The Scroll To action can be useful when using [Form steps](doc:form-steps), for
`}
diff --git a/docs/Getting started/examples-and-guides/show-button-on-condition.md b/docs/Getting started/examples-and-guides/show-button-on-condition.md
index 70eb46d5e..8007f7619 100644
--- a/docs/Getting started/examples-and-guides/show-button-on-condition.md
+++ b/docs/Getting started/examples-and-guides/show-button-on-condition.md
@@ -10,56 +10,36 @@ metadata:
next:
description: ''
---
-## Try it out!
+Use conditional UI when a button should only appear after a user has searched for an existing record.
-{`
-
-`}
-
-## Scenario
-
-Only show a 'create new record' button if the user has already searched the existing records for potential duplicates.
-
-{`
-Challenge:
-★☆☆☆☆
-`}
+## Build the search area
-***
+1. Add a [Data provider](doc:data-provider) for the `Employees` table
+2. Add a table inside the provider
+3. Add a form above the provider
+4. Add a text field to the form for the search term
+5. Add a filter to the provider using the form field binding
-## Steps
+## Build the create button
-1. Start by adding a Data Provider, set to fetch rows from the Employees table
-2. Add a table nested inside the Data Provider, with the data source as the Data Provider (not fetching data directly)
+1. Add a button under the provider
+2. Set the button text
+3. Add an action to open a modal
+4. Add a form block inside the modal to create a new employee
-
-3. Above the Data Provider component, add a a Form component (not to be confused with a Form Block).
-4. Add a text field to the form, setting the field to "email" and the label to "Search Employee Email"
+## Add the condition
-
-5. In the settings of the Data Provider, click to add a filter (Important: be sure to set the filter on the Data Provider itself, not the table)
-6. Add the filter `Email` is `Like` the `Binding` `{{ New Form.Fields.email }}`
+1. Open the button's `Conditions` tab
+2. Add a `Show Component` condition
+3. Set the condition to check whether the provider has zero rows
-
-7. Add a button underneath the Data Provider, with the button text to "Add new employee"
-8. Add a Settings Panel with "Ignore clicks outside" checked, and the size set to "Medium.
-9. Add a Form Block to the modal, set to create a new row in the Employees Table.
+The button only appears when the search returns no matches.
-
-10. In the button, set an On Click action to open the modal, and click save.
+## Try it out
- 
-11. In the Button component, head to the Conditions tab
-12. Add a condition to `Show Component` `IF` `{{ New Data Provider.Rows Length }}` `Equals` `Number` `0`
-13. Remember to save
-
-
-
-Now if a user tries to search for an employee that doesn't exist, they can create a new record.
-
-
-
-***
+{`
+
+`}
## App export
@@ -73,19 +53,17 @@ Now if a user tries to search for an employee that doesn't exist, they can creat
`}
diff --git a/docs/Getting started/examples-and-guides/table-rows-status-icon.md b/docs/Getting started/examples-and-guides/table-rows-status-icon.md
index 06ae34b25..a730db3a5 100644
--- a/docs/Getting started/examples-and-guides/table-rows-status-icon.md
+++ b/docs/Getting started/examples-and-guides/table-rows-status-icon.md
@@ -1,6 +1,6 @@
---
title: Table row status
-excerpt: Add an icon to graphically indicate the status of your records
+excerpt: Highlight a row status with conditional styling
deprecated: false
hidden: false
metadata:
@@ -10,46 +10,28 @@ metadata:
next:
description: ''
---
-## Try it out!
-
-{`
-
-`}
+Use conditional styling when a table cell should visually reflect a row status.
## Scenario
-In this example we have a table of sales records and we want to display an icon to show if the sale was made _Online_ or _Offline_.
+This pattern is useful when a field stores values like `Online` and `Offline`, or any other status values.
-The same approach could be applied for status based workflows, e.g. _Approved_, _In Progress_, _Complete_, etc.
-
-{`
-Challenge:
-★☆☆☆☆
-`}
+## Build the table
-***
+1. Add a [Table](doc:table) for the relevant data
+2. Hide any actions you do not need
-## Steps
+## Add the condition
-1. Add a table to your screen, and point it to the Sales table (available in the download link at the bottom of the page)
-2. In the Table settings, disable "Add rows", "Edit rows", "Delete Rows"
-3. In the Table settings, under columns, click the cog/sprocket icon for the Sales Channel
-4. Add a condition by clicking "No conditions set"
+1. Open the column settings for the status field
+2. Add a condition for the `Offline` value
+3. Set the cell background to red
+4. Duplicate the condition for the `Online` value
+5. Set the matching cell background to green
-
-5. Add the condition "Update `Cell` `Background color` to [red] if value `Equals` `Value` Offline"
+## Why this works
-
-6. Duplicate the condition, and adjust it to show green for "Online"
-7. Save
-
-
-
-
-
-
-
-***
+The table stays readable, but the status is easier to scan at a glance.
## App export
@@ -63,19 +45,17 @@ _Downloads may take a few seconds._
`}
diff --git a/docs/Getting started/examples-and-guides/update-date-field-on-change.md b/docs/Getting started/examples-and-guides/update-date-field-on-change.md
index c4b992ce4..60cfb1374 100644
--- a/docs/Getting started/examples-and-guides/update-date-field-on-change.md
+++ b/docs/Getting started/examples-and-guides/update-date-field-on-change.md
@@ -10,134 +10,31 @@ metadata:
next:
description: ''
---
-## Try it out!
-
-{`
-
-`}
+Use this pattern when one field should recalculate related date fields as the user changes a value.
## Scenario
-You're building an app and you want to set a project length in days, and then dynamically calculate start and end dates.
-
-{`
-Challenge:
-★★★☆☆
-`}
-
-***
-
-## Steps
-
-1. `Add component`: **Form**
-2. In the Settings Panel set *Schema* to 'Jobs'
-3. `Add component`: **Container**
-4. In the Settings Panel set the layout to flex
- 1. Set *Direction* to 'Row'
-
-
-
-5. `Add component`: **Number Field**
-6. Manually enter 'Direction' into the *Field* name and set *Label* to 'Days of work'
-7. Set *Default value* to 7
-8. `Configure validation`:
- 1. `Add Rule`: Min value 'Value' 1 *Cannot be less than one day!*
- 2. `Add Rule`: Max value 'Value' 365 *Cannot be more than 365 days!*
- 3. `Save`
-
-
-
-9. `Add component`: **Date Picker**
-10. In the Settings Panel select 'Works Start' from the *Field* dropdown
-11. Set *Label* to 'Works Start'
-12. Untick *Show time*
-
-
+This guide uses a duration field to update start and end dates automatically.
-13. [Duplicate](https://docs.budibase.com/docs/components#editing) the **Date Picker**
-14. Replace 'Start' with 'End' in the *Field* and *Label* settings
+## Build the form
-> 👍
->
-> Time to make the field values dynamically update!
+1. Add a [Form](doc:forms) for the `Jobs` table
+2. Add a number field for the duration
+3. Add a date picker for the start date
+4. Add a date picker for the end date
-15. In the Settings Panel under *On change*: `Define actions`
+## Add the actions
- 1. **Validate Form** - make sure to select the form in the dropdown
- 2. **Update Field Value**
+1. Add a `Validate Form` action to the duration field
+2. Add an `Update Field Value` action that calculates the start date
+3. Add a matching `Update Field Value` action for the end date
+4. Add a guard so the end date only updates when the start date exists
- 1. select the *Form* from the dropdown
- 2. select 'Set value' as the *Type*
- 3. select 'Works Start' as the *Field*
- 4. click the lightning bolt icon, and click on the `JavaScript` tab. Provide the following:
+Use bindings to read the current value and JavaScript to add or subtract days.
- ```javascript JavaScript
- //Source: https://stackoverflow.com/questions/563406/how-to-add-days-to-date
- Date.prototype.subtractDays = function(days) {
- var date = new Date(this.valueOf());
- date.setDate(date.getDate() - days);
- return date;
- }
+## Why this works
- return new Date($("Field Value")).subtractDays($("Jobs Form.Fields.Duration"));
- ```
-
-> 📘
->
-> The **Validate Form** action will prevent the **Update Field Value** from firing if there's any validation errors.
->
-> The special `$("Field Value")` binding is used to indicate that the [On change](https://docs.budibase.com/docs/text-inputs#on-change) value should be used, and not the value before the change.
->
-> Also note that you may need to replace the `$("Jobs Form.Fields.Duration")` binding to match your form name.
-
-
-
-16. Select the **Works Start Date Picker**. Under *On change*: `Define actions`
-
- 1. **Validate Form** - make sure to select the form in the dropdown
- 2. **Update Field Value** - similar to before, but this time we are setting the value of the 'Works End' field, and will add days instead of subtracting:
-
- ```javascript
- //Source: https://stackoverflow.com/questions/563406/how-to-add-days-to-date
- Date.prototype.addDays = function(days) {
- var date = new Date(this.valueOf());
- date.setDate(date.getDate() + days);
- return date;
- }
-
- return new Date($("Field Value")).addDays($("Jobs Form.Fields.Duration"));
- ```
-17. Finally click on the **Number Field**. Under *On change*: `Define actions`
-
- 1. **Validate Form** - make sure to select the form in the dropdown
- 2. **Continue if / Stop if** - 'Stop if' `{{ Jobs Form.Fields.Works Start }}` 'Equals' ""
- 3. **Update Field Value**:
-
- 1. select the *Form* from the dropdown
- 2. select 'Set value' as the *Type*
- 3. select 'Works End' as the *Field*
- 4. click the lightning bolt icon, and click on the `JavaScript` tab. Provide the following:
-
- ```javascript
- //Source: https://stackoverflow.com/questions/563406/how-to-add-days-to-date
- Date.prototype.addDays = function(days) {
- var date = new Date(this.valueOf());
- date.setDate(date.getDate() + days);
- return date;
- }
-
- return new Date($("Form.Fields.Works Start")).addDays($("Field Value"));
- ```
-
-> 📘
->
-> In this case we first check that the number of days is a valid number.
->
-> Next we use the [Stop if](https://docs.budibase.com/docs/chaining-actions#continue-if--stop-if) action to make sure a 'Works Start' date has been chosen, because we need to know the start date in order to calculate the end date.
-
-
-
-***
+The user edits one value, and the other dates stay in sync.
## App export
@@ -151,19 +48,17 @@ You're building an app and you want to set a project length in days, and then dy
`}
diff --git a/docs/Getting started/quickstart-budibase-ai.md b/docs/Getting started/quickstart-budibase-ai.md
index 8f41d4dac..04fc1bfaf 100644
--- a/docs/Getting started/quickstart-budibase-ai.md
+++ b/docs/Getting started/quickstart-budibase-ai.md
@@ -1,9 +1,7 @@
---
title: 'Quickstart: Budibase AI'
excerpt: >-
- This page will help you get Budibase AI up and running quickly so you can
- start generating code or database tables or creating LLM-powered automations
- in your Workspace
+ Set up Budibase AI and use it in columns, bindings, automations, and agents.
deprecated: false
hidden: false
metadata:
@@ -20,37 +18,51 @@ next:
slug: generating-javascript-with-ai
title: Generating JavaScript with AI
---
-## Enabling Budibase AI
+Connect a model provider in your workspace settings before you use Budibase AI.
-To start using Budibase AI, configure an AI provider in your Workspace.
+## Enable AI
-1. Open your Workspace **Settings**.
-2. Go to **Connections > AI models**.
-3. Choose a provider (for example Budibase AI, OpenAI, Anthropic, Google, Mistral, OpenRouter, or Groq).
+1. Open your workspace settings.
+2. Go to `Connections > AI models`.
+3. Choose a provider.
4. Enter the required credentials and model details.
-5. Save and enable the configuration.
+5. Save the configuration.
-AI features are workspace specific, so you will need to connect to these again in other workspaces if you wish to enable AI functionality.
+AI features are workspace specific. Repeat the setup in each workspace where you want AI enabled.
-For a full breakdown of the new setup area, see [Connections](doc:connections).
+If you are self-hosting, make sure your plan and license support Budibase AI.
-> 📘 If you are self-hosting and want to use Budibase AI, you need to make sure you are on an Enterprise plan and have a active license key in your Workspace. See the [Tenant management guide](doc:tenant-management).
+## Where you can use AI
-## Where You Can Use AI
+Budibase AI is available in several parts of the product:
-Once enabled, Budibase AI can assist in various areas of Budibase:
+* AI Columns for generating or transforming data
+* Binding fields for generating JavaScript
+* Cron job expressions
+* Agents for building AI assistants that can read data, use tools, and trigger automations
+* AI automation actions
+* Budibase DB table generation
-* [AI Columns](doc:ai-column)
- Automatically generate values for columns using AI, perfect for transforming, summarizing, or cleaning data.
-* [Generate JavaScript with AI](doc:generating-javascript-with-ai)
- You'll now see an AI icon in any field that supports bindings (e.g., visibility rules, dynamic values). Click it to open a prompt where you can describe what you need. Budibase AI will return usable JavaScript tailored to your input.
-* [Cron Job Expression Generation](doc:cronjob#configuring-the-cron-expression)
- In the settings area for the cron job, you'll see a field called "Generate Cron Expression with AI", simply type in your prompt and this will provide you with an expression that suits your needs.
-* [AI Automation Actions](doc:ai-automation-steps-overview)
- Triggerable automation actions that leverage AI, including classifying, generating, and categorising text, extracting document data, or executing custom prompts within automation rules.
-* [Generate Tables in Budibase DB](doc:ai-powered-table-generation)
- Leveraging Budibase AI to generate schemas for Budibase DB tables, which can be leveraged across your Workspace.
+## Practical guidance
-
+Use AI where it saves repetitive work.
-
+Good fits include:
+
+* Schema generation
+* Cleanup or summarisation tasks
+* JavaScript drafts
+* Automation prompts
+
+Review generated output before using it in production.
+
+## Related guides
+
+* [Agents](doc:agent-building-101)
+* [Agent instructions guide](doc:agent-instructions-guide)
+* [Agent tools and permissions](doc:agent-tools-and-permissions)
+* [Agent testing guide](doc:agent-testing-guide)
+* [AI Column](doc:ai-column)
+* [AI automation actions](doc:ai-automation-steps-overview)
+* [Generating JavaScript with AI](doc:generating-javascript-with-ai)
+* [Connections](doc:connections)
diff --git a/docs/Getting started/quickstart.md b/docs/Getting started/quickstart.md
index 7c3929154..3036c74da 100644
--- a/docs/Getting started/quickstart.md
+++ b/docs/Getting started/quickstart.md
@@ -10,15 +10,15 @@ metadata:
next:
description: ''
---
-This tutorial will take less than 5 minutes, and at the end, you will have successfully built a complete CRUD application.
+This quickstart shows how to build a basic CRUD app from sample data in a few minutes.
-We will use the [Sample data](doc:data-sources#sample-data), which includes a few tables with various data types to look at.
+We use the [Sample data](doc:data-sources#sample-data), which includes a few tables and common field types.
## **Getting started**
-1. Create a new account at [https://account.budibase.app/register](https://account.budibase.app/register) (onboarding takes a few seconds).
-2. Once registered, log in.
-3. Step through the first Workspace wizard, selecting *Budibase Sample data*.
+1. Create a new account at [https://account.budibase.app/register](https://account.budibase.app/register).
+2. Log in after registration.
+3. In the first Workspace wizard, select *Budibase Sample data*.
Expected result:
@@ -29,13 +29,13 @@ Expected result:
## **Agents**
-If agents are your main use case, configure them early in the same workspace.
+If Agents are part of your setup, configure them in the same workspace before you build further.
| Step | What to do |
| :--- | :--------- |
| Configure models | Add an AI provider under **Settings > Connections > AI models** |
| Create an agent | Open **AI > Agents**, create an agent, and connect a model |
-| Test and deploy | Validate behavior, then use `Set live` to deploy |
+| Test and deploy | Validate the agent, then use `Set live` to deploy |
| Add user access | Enable Agent Chat or embed chat with the Chatbox component |
Useful guides:
@@ -46,79 +46,73 @@ Useful guides:
## **Data**
-Most of the columns in our sample data, for example *First Name*, *Last Name* and *Email*, are [Text](doc:text) data types, which is the most common.
+Most sample columns, such as *First Name*, *Last Name*, and *Email*, use the [Text](doc:text) data type.
-Other types include [Number](doc:number), [Date/Time](doc:datetime), and more.
+Other common types include [Number](doc:number), [Date/Time](doc:datetime), and more.
-The values of some fields are highlighted in a coloured pill. This indicates the value to be either a [Multi-select](doc:multi-select) value such as *Employee Level* in the **Employees** table, or a [Relationships](doc:relationships) link such as *Assigned* in the **Jobs** table.
+Some fields display in a coloured pill. This usually means the field is a [Multi-select](doc:multi-select) value such as *Employee Level* in the **Employees** table, or a [Relationships](doc:relationships) link such as *Assigned* in the **Jobs** table.
-In Budibase, data sources are shared across their Workspace. This means that we can utilize them within any App or Automation in the same Workspace.
+Data sources are shared across the Workspace, so you can use them in any app or automation.
## **External datasource**
-If you want to connect to external datasources, have a look at our [Datasources](doc:data-sources) section for a list of available connectors.
-Further information can be found in the [Introduction to Data](doc:data) page.
+If you want to connect to external data sources, see [Data sources](doc:data-sources) for the available connectors.
+For more detail, see [Introduction to Data](doc:data).
***
## **Apps**
-As we have some data, we can now start building out end-user apps. This is done in the Apps section, which can be navigated to via the tab on the left-hand side of the page.
+Now that the workspace has data, you can start building an app in the Apps section.
-Here, we’re prompted to create our first app.
-
-Within the app builder, the left-hand panel includes two core areas for building out your apps:
+The left-hand panel includes two core areas:
1. [Screens](doc:screens)
2. [Components](doc:components)
-When building apps, you will mostly be focused on the *Screen* and *Component* views.
+Most app work happens in the *Screen* and *Component* views.
-When the *Screen* is selected in the component tree, basic settings are available in the right-hand side panel.
+When a *Screen* is selected in the component tree, its settings appear in the right-hand panel.
-You can access navigation settings under `Navigation`.
+Navigation settings are available under `Navigation`.
### **Autogenerated screens**
-To get started quickly, click `Add screen`. Next, select `Table` to create new CRUD (Create, Read, Update, Delete) screens for each of your tables.
-
-These autogenerated CRUD screens make use of the [Table](doc:table) and [Side panels](doc:side-panel) for fast, all-in-one data interaction.
+To get started, click `Add screen` and select `Table` to generate CRUD screens for each table.
-When auto-generating, screens inherit the access level from the underlying data source.
+These screens use the [Table](doc:table) and [Side panels](doc:side-panel) components for fast data interaction.
-You can read more about [screen access levels](doc:screens#access-level), but in this tutorial, we will stick to the defaults.
+Generated screens inherit the access level from the underlying data source.
-Upon clicking `Done`, a new screen is generated for each table selected.
+For this tutorial, keep the default [screen access levels](doc:screens#access-level).
### **Components**
-With the screens created, you can view and edit the components for each.
+Once the screens are created, you can view and edit their components.
-With the Table component selected, you can edit its settings via the Settings Panel. Here you can change the *Data Source*, *Filters*, *Column*, and more. These settings are explained on the [Table](doc:table) page.
+With the Table component selected, use the Settings panel to change the *Data Source*, *Filters*, *Column*, and more. See the [Table](doc:table) page for details.
-You can also click the `+` icon in the bottom right to add another component to your screen. You can view a list of available core [Components](doc:components) and learn more about [Data in design](doc:working-with-data).
+You can also click the `+` icon in the bottom right to add another component. For a list of core components, see [Components](doc:components). To bind data in the builder, see [Data in design](doc:working-with-data).
### **Blocks**
-In Budibase, there is a category of components known as [Blocks](doc:blocks), which are collections of pre-built components designed to make building your apps easier and faster.
-
-A list of them can be found in the *Blocks* section of the components view.
+Blocks are pre-built groups of components that speed up app building.
-Sometimes you will find yourself needing to make a fine-grain tweak that cannot be done through the settings panel alone.
+You can find them in the *Blocks* section of the components view.
-If you want to edit the form fields in the side panel of a Form Block, for example, click the [Eject](doc:blocks#ejecting-blocks) button to pop open the block, exposing its constituent components.
+If you need to make a change that is not available in the Settings panel, use [Eject](doc:blocks#ejecting-blocks) to open the block and edit its components directly.
***
## **Preview and Publish**
-During development you may wish to test out your app so far. Click `Preview` in the top-right to open the app in a modal. The app preview always opens the screen selected within the builder.
+During development, use `Preview` to test the app in a modal. Preview opens the screen selected in the builder.
-You will also notice that navigation links are automatically added to the nav bar when autogenerated screens are created.
+Autogenerated screens also add navigation links to the nav bar.
-To view the app with a different permission level, change the `View as` option.
+Use `View as` to test a different permission level.
-When you are happy with your app, use `Set live` to deploy your app into production.
+When you are ready, use `Set live` to deploy the app.
| Control | Where | Outcome |
| :------ | :---- | :------ |
@@ -130,17 +124,17 @@ When you are happy with your app, use `Set live` to deploy your app into product
## **Automate**
-Automations are not needed for all use-cases, however they offer powerful workflow options such as [Cron jobs](doc:cronjob) and [Webhooks](doc:webhook) integrations.
+Automations are optional, but they add workflow options such as [Cron jobs](doc:cronjob) and [Webhooks](doc:webhook).
-In Budibase, Automations can interact with all data and apps in their Workspace.
+Automations can interact with all workspace data and apps.
-More information can be found in the [Automations](doc:contextual-bindings) page.
+For more detail, see [Automations](doc:contextual-bindings).
***
## **Settings**
-The settings tab includes a variety of options for your workspace. These include:
+The Settings tab includes workspace options such as:
* [Automations](doc:logs)
* [Backups 🔒](doc:backups-1)
@@ -151,9 +145,9 @@ The settings tab includes a variety of options for your workspace. These include
## **Further exploration**
-At this point, you can see the speed and convenience with which Budibase allows you to get your users interacting with data.
+At this point, you have the basics needed to build an app around your data.
-There is, however, much more to explore, and a broad range of possibilities. If you wish to add logic and interactivity to your apps, these docs pages may be a good place to check out:
+If you want to add more logic or interactivity, these pages are a good next step:
* [Forms](doc:forms)
* [Bindings](doc:introduction-to-bindings)
@@ -163,4 +157,4 @@ There is, however, much more to explore, and a broad range of possibilities. If
## **The Budibase community**
-If you have any questions or want to browse what other people have been building, please reach out to us on [GitHub Discussions](https://github.com/Budibase/budibase/discussions) or join our [Discord server](https://discord.com/invite/ZepTmGbtfF).
+If you have questions or want to see what others have built, visit [GitHub Discussions](https://github.com/Budibase/budibase/discussions) or join our [Discord server](https://discord.com/invite/ZepTmGbtfF).
diff --git a/docs/Getting started/templates/change-requests.md b/docs/Getting started/templates/change-requests.md
index acd937b57..6df91a0ab 100644
--- a/docs/Getting started/templates/change-requests.md
+++ b/docs/Getting started/templates/change-requests.md
@@ -1,7 +1,7 @@
---
title: Change Requests
excerpt: ''
-deprecated: false
+deprecated: true
hidden: false
metadata:
title: ''
@@ -10,352 +10,111 @@ metadata:
next:
description: ''
---
-Our change management template offers a fully functional solution for handling incoming requests. Use it as a ready-to-deploy app or customize any aspect of its data, design, and logic to suit your own unique processes.
+Use the Change Requests template to manage submissions through a simple approval flow. It includes the core data model, role-based access, and the automations needed to move requests from submission to final decision.
-In this guide, we outline exactly what our template does, how it’s built, and some of the key areas where you can put your own stamp on it using Budibase.
+To use this template, [open it here](https://budibase.app/builder?template=app/change-request).
-To use this template, [click here](https://budibase.app/builder?template=app/change-request).
+## What this template includes
-Let’s jump right in.
+The template is built for request intake, review, and auditability. It gives you:
-## What does this template do?
+* A requester-facing form for creating submissions
+* Manager and reviewer views for triage and approval
+* A status history table for tracking every change
+* Automations for notifications and status updates
+* Role-based permissions for requesters, managers, reviewers, and admins
-Change requests are a key part of managing projects, processes, or services. This presents a few challenges:
+## Data model
-1. Gathering request data.
-2. Enforcing approval workflows.
-3. Documenting decisions and outcomes.
-4. Handling follow-on actions, including communicating with stakeholders.
+The app uses two Budibase DB tables and the built-in `Users` table.
-Our solution is based around a two-step approval workflow. It’s built on two tables in Budibase DB.
+| Table | Purpose |
+| :--- | :--- |
+| `Change Request` | Stores the submitted request and the fields needed during review |
+| `Status History` | Stores a record of each status change for audit purposes |
-Here’s a summary of core functionality.
+### `Change Request`
-### CRUD Operations
+Typical columns include:
-Users can take distinct CRUD actions across the application, according to their role. Different users can create, read, update, or delete specific data depending on their needs within the change request workflow.
+* `Title`
+* `Description`
+* `Change type`
+* `Submitted date`
+* `Date required`
+* `Status`
+* `Submitted by`
+* `Priority`
+* `Reason for change`
+* `Attachments`
+* `References`
+* `Date signed`
+* `Status history`
+* `Review signature`
+* `Rejection reason`
+* `Completion date`
+* `CrId`
-In Budibase, user permissions regarding CRUD actions are defined in the Data section. Once we’ve configured which data users can interact with, we can autogenerate CRUD UIs that reflect these permissions.
+### `Status History`
-Our app UI is made up of a range of task-specific forms and tables. For example, enabling users with the **Requestor** role to add new records to our requests table:
+This table records each update to a request status.
-
+* `New status`
+* `Previous status`
+* `Timestamp`
+* `Changed by`
+* `Change request`
-### Approval workflows
+## Roles and access
-When a request is submitted, its **status** attribute is automatically set to **Submitted** using Budibase’s **default values**. At this point, a **Row Action** automation is triggered, notifying a **Manager** that a new request needs their attention.
+The template uses four access patterns:
-The manager then has the option to Accept or Reject the request using separate Row Actions. When a request is accepted by the manager, its status is updated to **In-Review**.
+* `Requesters` create requests and edit their own submissions
+* `Managers` review requests with `Submitted` status
+* `Reviewers` handle requests that have moved to `In Review`
+* `Admins` can manage the app and all connected data
-
+Permissions are configured through views and column-level access:
-At this point, it is sent to all users with the **Reviewer** role.
+* `My Requests` returns only rows created by the current user
+* `Manager Approval` returns rows with `Submitted` status
+* `Reviewer Approval` returns rows with `In Review` status
+* Admin access covers both tables with full read and write permissions
-Reviewers are able to provide the final sign-off for the request.
+## Screens
-### Tracking change statuses
+The template uses autogenerated screens where possible.
-A key part of change management is documenting exactly what has been adopted and who signed it off.
+* Requesters use a table and form workflow to create and update their own requests
+* Managers and reviewers work from a shared review screen with approval and rejection actions
+* Admins use a dedicated screen to inspect request history and supporting details
-Our template features a dedicated **Status History** table. Each time a **Request’s** status attribute changes, this table is updated with a new row. The new status, previous status, user, and timestamp are automatically recorded.
+When you adapt the template, keep the screen structure aligned with the role that uses it. That makes the workflow easier to understand and reduces unnecessary navigation.
-This provides a clear record of how requests progress through our approval process, providing oversight and auditability.
+## Automations
-We can view these records using a dedicated History UI, which is accessible only to users with the **Admin** role.
+The template includes automations for the core workflow:
-
+* `Submitted Change Request` runs when a request is created and notifies managers
+* `Cancel` updates the current request and confirms the change to the requester
+* Manager and reviewer approval flows update status and write supporting notes
+* Status changes can also write to `Status History` for auditability
-### Automated emails
+If you add new statuses or extra review stages, update the related automations at the same time.
-As a change request progresses through our workflow, it’s important to keep stakeholders up to date with developments.
+## Customizing the template
-Our application utilizes Budibase’s automated email capabilities as the **status** of requests changes, notifying users when there’s something that requires their attention, as well as the final outcome of requests.
+Start with these changes:
-Take a look at our documentation on [working with email automations](https://docs.budibase.com/docs/send-email).
+1. Rename the tables and roles to match your process.
+2. Adjust the status values to reflect your approval stages.
+3. Update the requester, manager, reviewer, and admin screens.
+4. Add or remove fields from the request form as needed.
+5. Tighten row-level permissions before using the template in production.
-### User roles
+## Related guides
-Our change management workflow utilizes three custom access roles, along with Budibase’s default Admin user type.
-
-These are used to determine data exposure and permitted actions for end users.
-
-The permissions associated with each of our roles are as follows.
-
-**Requesters** are able to submit change requests or view and edit their own previous submissions.
-
-**Managers** inherit all permissions from the **Requester** role. On top of this, they can review all requests with a **Submitted** status and trigger approve or reject row actions. When a **Manager** approves a request, it is passed to the next stage of our approval workflow.
-
-**Reviewers** also inherit permissions from **Requesters**, and from **Managers**. They can then carry out row actions for final approval or rejection and provide additional information.
-
-**Admins** can manage application settings, users, and roles. They also have full CRUD permissions across all connected data.
-
-
-
-## How does it work?
-
-Our templates are designed to help you get started with Budibase. They’re fully working solutions, but you can customize them as much as you need to suit your change request management workflows.
-
-Let’s check out how our change request app is built.
-
-### Data structure
-
-In Budibase, the Data section is at the core of the app-building experience. In addition to configuring our app’s data, we can use this to set access rules, define user actions, and even generate UIs.
-
-Check out our documentation on [Budibase DB](https://docs.budibase.com/docs/budibasedb) to learn more.
-
-Our template is built on two tables within Budibase’s internal database. It also utilizes several role-specific views to control which data different kinds of users can interact with across these.
-
-Here’s how this works.
-
-#### Tables
-
-Our application utilizes two database tables and Budibase’s internal **Users** table.
-
-One is **Change Request,** which stores all information about incoming requests and additional details provided during the approval process.
-
-The other is **Status History**, which stores information about how the Status attribute of individual Change Request rows has changed.
-
-The **Change Request** table stores the following attributes:
-
-* **Title** - Text,
-* **Description** - Long Form Text,
-* **Change type** - Single Select,
-* **Submitted date** - Date,
-* **Date required** - Date,
-* **Status** - Single Select,
-* **Submitted by** - Single User,
-* **Priority** - Single Select,
-* **Reason for change** - Long Form Text,
-* **Attachments** - Attachment,
-* **References** - Text,
-* **Date signed** - Date,
-* **Status history** - Relationship,
-* **Review signature** - Signature,
-* **Rejection reason** - Long Form Text,
-* **Completion date** - Date,
-* **CrId** - Number.
-
-Meanwhile, the **Status** **History** table maintains a record of updates to the **Status** attribute on our **Change Request** table, with the following attributes:
-
-* **New status** - Text,
-* **Previous status** - Text,
-* **Timestamp** - Date,
-* **Changed by** - Single User,
-* **Change request** - Relationship.
-
-Whereas the **Change Request** table is primarily populated manually by users, entries on the **Status History** table are created automatically when the **Status** is updated. We’ll see how this works a little later.
-
-#### Relationships
-
-Our data model relies on a relationship between our two database tables and between each of these and Budibase’s internal Users table.
-
-The **Relationship** table allows us to configure links between rows across tables in BudibaseDB. Our application uses a **One-To-Many** relationship from our **Change Request** table to **Status History**.
-
-
-
-Budibase also allows us to set up relationships between our database and the internal **Users** table. For instance, to assign a row to a particular record, or track who has created a row.
-
-This is also helpful when defining data permissions for specific users. For instance, only allowing users to view their own submissions or assigned rows.
-
-We can do this with two special data types, the **Single User** and **Multi User** columns.
-
-Across our two tables, we’ve included several **Single User** columns, primarily to track actions. For instance, the **Submitted by** column on our **Change request** table.
-
-
-
-#### Views
-
-**Views** are subsets of data across one or more tables that we can create for a specific purpose or function by setting rules for which rows, columns, or permitted actions to include. We can then set access rules, defining which **Views** can be used by different user roles.
-
-We’ve created three **Views** based on our **Change Request** table to provide **Employees**, **Managers**, and **Reviewers** with data exposure that’s appropriate for their roles.
-
-**Employees** can submit data or view their own previous submissions using a **View** called **My Requests**. This uses a filter expression to only return the rows where the **Submitted by** attribute is set to the **Current User** in app’s front-end.
-
-
-
-Under the **Columns** tab, we’ve granted **Write** permissions for all attributes required to create an initial change request, as well as **Read** access for the **Status** column.
-
-
-
-Our **Manager Approval** and **Reviewer Approval** views include rows irrespective of which user created them. Instead, they’re filtered by their current **Status**, enabling these users to access requests that are at an appropriate stage in our workflow for their role.
-
-**Managers** can access rows where the **Status** is **Submitted** while the **Reviewer Approval** View returns requests that are **In Review**.
-
-
-
-They also each have **Read** and **Write** permissions for the columns that are required for each of them to respond to requests.
-
-
-
-Users with the **Admin** role have full **Read** and **Write** permissions for both our **Change Request** and **Status History** tables.
-
-### User interfaces
-
-Next, let’s check out how our template’s front end is built. Check out our docs page on [working with screens within Budibase](https://docs.budibase.com/docs/screens).
-
-Since users primarily need to either create database entries or view and update existing ones, our app relies heavily on Budibase’s autogenerated **Form** and **Table** layouts.
-
-These inherit the access roles of the tables or views they’re based on.
-
-For **Employees**, the app’s home screen is **CRUD** **UI**. This has been auto-generated from the **My Requests** View, using the **Table** layout with separate form screens.
-
-
-
-Our template uses a single screen for handling both **Manager** and **Reviewer** actions. This displays relevant **Change Request** records for each type of user within a **Table** UI where they can open modal forms to **Approve** or **Reject** submissions and provide notes.
-
-
-
-In the Budibase builder, this screen actually contains two separate tables. We’re using conditionality rules to determine which one to display in the end-user app, depending on the current user’s role.
-
-Take a look at our documentation on [working with data in design](https://docs.budibase.com/docs/working-with-data) to learn more about how this works.
-
-
-
-The specific **form** that loads within the **modal** is determined by which action the user has selected. This works by setting an in-app state when they initially hit **Approve** or **Reject** and applying a conditionality rule to our forms based on this.
-
-
-
-Lastly, our **Administration** screen is also based on an autogenerated **Table** layout with some important modifications. Firstly, we’ve nested our table inside a form and added filter expressions based on user selections in the provided form fields.
-
-This means that our table can be filtered from the front-end, so **Admins** can more easily find the history of specific **Change Request** records.
-
-
-
-Secondly, rather than opening an **Update** form, when a user clicks a row, it opens a custom screen to expand the details of the specific **Change Request**.
-
-This utilizes a series of **Markdown Viewer** components and **Containers** to display the data from the original request, as well as a **Read-Only** table of all related **Status History** rows.
-
-
-
-### Automations
-
-In Budibase, Automations are used to define sequences of steps that can be triggered by user actions, database events, cron expressions, or other triggers. Take a look at our [automation docs](https://docs.budibase.com/docs/automation-steps) to learn more.
-
-We’ve used several automation workflows across our template to handle key interactions with our data, as well as notifying users of how their submissions are progressing.
-
-Many of these rely on Budibase’s **Row Actions** functionality. A Row Action is a user-triggered automation rule that relates to a particular entry in a database or view. Others are triggered when a database entry is created or updated.
-
-Let’s check these out.
-
-#### Request submission and cancellation
-
-Our first Automation is called **Submitted Change Request**. This uses a **Row Created** trigger on our **Change Request** table. So, it will run each time a user creates a new request.
-
-This flow contains three steps. First, we use a **Query Rows** action to return all rows in the **Users** table with their role set to **Manager**. Second, we use some custom JavaScript to pass these users’ emails into an array.
-
-Thirdly, we use an **Email** action to notify all **Managers** that there’s a new request that requires their attention.
-
-
-
-We’ve also included a **Cancel** row action. This uses an **Update Row** step to change the **Status** attribute of the trigger row to **Cancelled**, before emailing the original submitter to confirm that this has been successful.
-
-This is triggerable from the front-end using a dedicated **Cancel** button within the **Update Form** for **Requesters**.
-
-
-
-#### Approval/rejection
-
-**Row Actions** are also central to our review and approval workflow.
-
-Specifically, we’ve created two sets of **Approve** and **Reject** actions for our **Manager** and **Reviewer** roles. These follow the same pattern but with a couple of important differences.
-
-**Manager: Approve** sets the **Status** of the original request to **In Review**, before querying the **Users** table for entries with the **Reviewer** role, passing their emails to an array, and notifying them that the request needs their attention.
-
-**Manager: Reject** updates the **Status** to **Rejected** and notifies the original submitter.
-
-Both of these are triggerable from the **Managers** version of our review screen, using button actions.
-
-
-
-**Reviewer: Approve** and **Reviewer: Reject** follow the same pattern as **Manager: Reject.**
-
-These update the **Status** to **Approved** or **Rejected**, respectively. Both then send an email notification of this update to the original **Requester**.
-
-They’re triggerable from the **Reviewer** UI.
-
-
-
-#### Change history
-
-Our final Automation works slightly differently, utilizing a **Row Update** trigger for our **Change Request** table. This means that it will run each time a row is updated, regardless of how this change was initiated.
-
-When this is triggered, it carries out three actions.
-
-First, we use a **Query Rows** action with a filter set to return all existing **Status History** rows that are related to the **Change Request** row in our trigger.
-
-Next, we use a **Condition** step so that our automation only continues if the most recent returned row does not have the same **Status** as our trigger row.
-
-Lastly, we use a **Create Row** action to add a record to our **Status History** table, recording the current status, previous status, timestamp, and the user who made the update.
-
-## Customizing our change request template
-
-Budibase templates are fully functional apps, but they’re also designed with customization in mind.
-
-Let’s think about some of the key ways that you can tailor your application to match your internal change management processes.
-
-### Updating the data model
-
-Budibase apps center around the Data section. This isn’t just where we set up the data our app connects to. It’s also a centralized location for managing access and permissions or generating screens and automations.
-
-At the most basic level, we might want to make adjustments to existing attributes within our data model. For instance, we could add additional options to our **Status** column to reflect a more complex approval process.
-
-Alternatively, we could remove certain attributes or add entirely new ones. For example, we could create a **Number** column called **Estimated Cost** on our **Change Requests** table using the plus icon.
-
-
-
-Alternatively, you might want to make more wholesale changes to our app’s backend, such as entirely swapping out the database.
-
-Budibase offers connectivity for a huge range of external datasets. We can even connect to SQL databases and interact with them in the Data section, exactly as we would in Budibase DB.
-
-We could then recreate our role-specific views and connect these to our existing screens and automations. Alternatively, we might recreate these from scratch using the **Generate** button.
-
-### Adding and altering user roles
-
-Our change management app relies on Budibase’s customizable RBAC system. If we wanted to create a more sophisticated approval flow, we could start by adding additional user roles.
-
-Say we wanted to update our approval flow so that the CTO can fast-track requests, by setting their status to **Approved**, regardless of their current status. We’d start by creating a new user role called **CTO**.
-
-RBAC within Budibase applications utilizes visual branches to establish hierarchies and handle permission inheritance.
-
-We want our CTO to inherit all of the permissions that our **Reviewer** role has but with access to additional data and actions.
-
-Therefore, we’re placing our CTO in the same branch as our existing roles, to the right of **Reviewer**.
-
-
-
-### Adding your own business rules
-
-After adding a new role, we need to define which data and actions they’ll be able to access. Let’s carry on with the example of only allowing our CTO to approve high-priority requests.
-
-Currently, our **Reviewers** and **Managers** have permissions to view and approve all requests with certain **Statuses**. Our CTO will be able to approve any request, regardless of its current status.
-
-Then, we’ll create a new view and call it **CTO** **Approval**. We’ll assign the **Access** to our new **CTO** role.
-
-Here, we want to recreate our **Reviewer View** but omit the filtering expression for the **status** attribute.
-
-
-
-### Creating new user actions
-
-Currently, our **Reviewers** approve or reject requests, using **Row Actions**, as we saw earlier. We’ll need to set up similar actions for our new **CTO Approval** view.
-
-We’ll start by hitting **Row Actions** and creating an action called **CTO: Approve**.
-
-
-
-Within the Automation section, we can follow the same pattern as our existing Automations, adding the following steps:
-
-* **Update Row** - Updating the original **Change Request’s** Status to approved.
-* **Email** - To notify the original Requester of this update.
-
-We could also add a corresponding **CTO: Reject** rule using the same process.
-
-### Modifying our screens
-
-Lastly, we’ll need to add an appropriate UI for our CTO role. This will essentially match our existing **Managers** and **Reviewers** UI with our updated data view.
-
-We can achieve this by duplicating one of the existing tables on our Review screen, and updating the Data Source for the new one to our CTO View, making sure to also update the corresponding conditionality rule and button actions.
-
-
-
-We’ll then repeat this process to provide conditional forms for our **CTO: Approve** and **CTO: Reject** row actions, mimicking our current settings, including setting an app state as one of the initial button actions.
+* [Budibase DB](https://docs.budibase.com/docs/budibasedb)
+* [Working with screens](https://docs.budibase.com/docs/screens)
+* [Working with email automations](https://docs.budibase.com/docs/send-email)
+* [Working with data in design](https://docs.budibase.com/docs/working-with-data)
diff --git a/docs/Getting started/templates/expense-approval.md b/docs/Getting started/templates/expense-approval.md
index 752d9cf63..a13428f70 100644
--- a/docs/Getting started/templates/expense-approval.md
+++ b/docs/Getting started/templates/expense-approval.md
@@ -1,7 +1,7 @@
---
title: Expense Approval
excerpt: ''
-deprecated: false
+deprecated: true
hidden: false
metadata:
title: ''
@@ -10,314 +10,98 @@ metadata:
next:
description: ''
---
-Our expense approval template offers a ready-to-use solution for enabling employees to submit claims for review and authorization. However, it’s also fully customizable, providing a basis for creating your own bespoke solution in Budibase.
+Use the Expense Approval template to manage employee claims through a straightforward submission and approval process.
-In this guide, we’re outlining how our template works, how it’s built, and a few examples of changes you could make to reflect your own internal expense processes.
+To use this template, [open it here](https://budibase.app/builder?template=app/expense-approval).
-To use this template, [click here](https://budibase.app/builder?template=app/expense-approval).
+## What this template includes
-Let’s jump in.
+The template is built around:
-## What does this template do?
+* Employee expense submissions
+* Manager review and approval
+* Receipt uploads for supporting evidence
+* Status updates for submitted claims
+* Email notifications when a claim is resolved
-Handling expenses is one of the most common administrative tasks across businesses in all industries. At the most basic level, this involves:
+## Roles and access
-1. Enabling employees to submit structured expense data.
-2. Routing submitted expenses to the appropriate authority for review.
-3. Applying defined business logic to approve or reject submissions.
-4. Recording outcomes and communicating them to stakeholders.
+The app uses two primary role groups:
-Our template is built around a simple approval workflow with two types of users.
+* `Employees` create and manage their own claims
+* `Managers` review all claims and approve or reject them
+* `App Admins` manage the underlying data and app configuration
-**Employees** can submit itemized claims relating to specific company **events**.
+Keep access rules aligned with the claim lifecycle. Employees should only see their own submissions, while managers should see everything they need to review.
-Their **Managers** can then view, review, and approve these claims.
+## Data model
-This is built on top of our internal database.
+The template uses three internal tables.
-Let’s break down the core functionality.
+| Table | Purpose |
+| :--- | :--- |
+| `Events` | Stores claimable events and supporting details |
+| `Expense Claim` | Stores each claim submission |
+| `Expense Item` | Stores the individual line items and receipts |
-### CRUD Operations
+### Relationships
-The core of our app is allowing different types of users to carry out defined CRUD operations on our underlying data. This relies on providing users with differing permissions to access data or carry out actions subject to their roles.
+The main relationships are:
-Firstly, **Employees** can view expenseable events and create new claims using a dedicated data collection form.
+* `Expense Item` links to `Expense Claim`
+* `Expense Claim` links to `Events`
+* `Expense Claim` links to `Users` for submitter and reviewer ownership
-
+Use `Relationship` columns for table links and `Single User` columns for ownership fields.
-They can also view their own previous claims, including their current statuses, but not the claims of their colleagues.
+## Workflow
-Within existing claims, they can update the **Status** attribute to **Cancelled**.
+The claim flow is:
-Users with the higher **Manager** role inherit all permissions from **Employees**. On top of this, they can view all claims, as well as carrying out update actions, enabling them to approve or reject submissions.
+1. An employee creates a claim from an event.
+2. The employee adds line items and receipts.
+3. The claim is submitted for review.
+4. A manager approves or rejects it.
+5. The employee receives a notification about the result.
-
+Employees can also cancel their own claims before they are resolved.
-### Receipt uploads
+## Screens
-To help validate claims and maintain effective records, users can submit receipts with their expense submissions.
+The template uses role-specific screens for common tasks:
-Our submission form features a **File Upload** component, which users can use to provide evidence supporting their claims.
+* Employees browse events, create claims, and review their own submissions
+* Managers review claim details and apply approval actions
+* Admins manage the event table and supporting data
-This is stored in an **Attachment** column alongside each individual expense item.
+Use autogenerated screens where possible, then tighten the layout and permissions for each role.
-### Approval workflows
+## Automations
-Our template is based around a simple, single-step approval workflow, with one class of users responsible for authorizing all expenses. This works by updating the **Status** attribute of the original claim to either **Approved** or **Rejected**.
+The main automations cover:
-These are performed using **Row Actions**, enabling approvers to respond to expense claims at the press of a button, rather than manually updating the **Status** attribute.
+* Claim submission
+* Claim cancellation
+* Approval and rejection
+* Email notifications to the submitter
-### Tracking change statuses
+If you add a new claim status or a new review step, update the row actions and notifications together.
-Within any approval workflow, tracking the status of submissions as they progress is critical.
+## Customising the template
-**Employees** can view their previous expense claims to check if the status has updated when a reviewer responds. As we’ll see in the next section, they’ll also be notified when the **status** of their claim is updated.
+Start by adjusting:
-
+1. Claim statuses
+2. Expense categories
+3. Event fields
+4. Role permissions
+5. Approval messaging
-### Automated emails
+If you swap in a different data source, keep the claim, item, and event relationships intact.
-A key time sink within approval processes is handling communications. Specifically, notifying users that something needs their attention. To solve this problem, our template utilizes email automations triggered by specific user actions.
+## Related guides
-In our workflow, this occurs at the point at which an expense claim receives a response.
-
-When this happens, the user who initially submitted the request is emailed, informing them of the outcome.
-
-This utilizes **Email** actions within the respective **Reject** and **Approve** Row Actions. We’ll examine how **Row Actions** work a little later.
-
-Take a look at our documentation on [working with email automations](https://docs.budibase.com/docs/send-email).
-
-## How does it work?
-
-Now that we have an understanding of how our template functions from the perspective of end-users, we can move on to thinking about how it’s actually built.
-
-Within Budibase, app-building is divided into separate concerns. Let’s check out how our template works across each section of the Budibase builder.
-
-### User roles
-
-Budibase’s custom RBAC tools allow us to configure the exact data, screens, and actions that different users are able to access.
-
-We can add or edit roles using our visual RBAC editor and grant them access to specific tables or Views within Budibase’s Data section.
-
-
-
-Our template utilises three distinct roles to control how user cohorts interact with data across our apps.
-
-**Employees** are able to view expensible events, submit itemized expenses, and view the current state of their own previous submissions. They can also update or cancel claims that they have created but not submitted.
-
-**Managers** can view, review, and respond to all **Employees** requests.
-
-**App Admins** have full CRUD access across all data tables.
-
-We’ll see the database Views we’ve created for each in the next section.
-
-### Data structure
-
-As we said earlier, our application is built on top of Budibase’s internal database. Check out our documentation on [Budibase DB](https://docs.budibase.com/docs/budibasedb) to learn more.
-
-Here’s how our data model works.
-
-#### Tables
-
-Our app utilizes three internal tables:
-
-1. **Events** - storing information relating to company events which expenses can be claimed against.
-2. **Expense Claim** - storing information about employees’ expense submissions.
-3. **Expense Item** - storing information about individual items within expense claims.
-
-Additionally, our template’s data model uses the **Single User** data type to establish relationships between our data and Budibase’s internal **Users** table, enabling us to assign rows to specific end users.
-
-The Events table stores columns with the following names and data types:
-
-* **Title** - Text,
-* **Description** - Long-Form Text,
-* **Location** - Text,
-* **Start Date** - Date,
-* **End Date** - Date,
-* **Claim Deadline** - Date,
-* **Max Claim** - Number,
-* **Claims** - Relationship,
-* **Image** - Single Attachment.
-
-The Expense Claim table stores:
-
-* **Summary** - Formula,
-* **Submitter** - Single User,
-* **Reviewer** - Single User,
-* **Status** - Single Select,
-* **Date** - Date,
-* **Expense Items** - Relationship,
-* **Event** - Relationship,
-* **Total** - Number,
-* **Submitter Notes** - Long-Form Text,
-* **Reviewer Notes** - Long-Form Text.
-
-Lastly, our Expense Item table stores the following attributes:
-
-* **Description** - Text,
-* **Price** - Number,
-* **Category** - Single-Select,
-* **Claim** - Relationship,
-* **Receipt** - Single Attachment.
-
-We’ll check out some examples of how you might want to modify these later.
-
-#### Relationships
-
-Our three tables are linked using Budibase’s Relationship data type.
-
-This allows us to define how individual entries across each of our tables relate to each other. We can then utilize attributes from directly related tables across **View Joins** within Budibase’s Data section or throughout our Designs and Automations.
-
-Our app relies on two Relationships:
-
-* **A Many-To-One Relationship** from the **Expense Item** table to the **Expense Claim** table.
-* **A Many-To-One Relationship** from the **Expsense Claim** table to the **Events** table.
-
-
-
-As we said a second ago, we also use **Single User** attributes to link specific pieces of data to users within Budibase’s internal **Users** table.
-
-There are two instances of this in our app, both within the **Expense Claim** table:
-
-* **Submitter** - An attribute that links each entry to the **User** that initially created the request. The value of this is set automatically, using the *default to current user* option.
-* **Reviewer** - An attribute that assigns records the **Reviewer** of the expense request.
-
-#### Views
-
-A **View** is a subset of our data that’s used to define which rows, columns, actions, or aggregated data specific users can access across one or more tables.
-
-In Budibase, these can be used to centrally configure all aspects of how our end-users interact with connected data within the Data section. Once we’ve created a view, we can use it to autogenerate role-specific screens and automations in just a few clicks.
-
-Our expense approval app utilises several views. Here’s a summary of the views we’ve created across our three tables.
-
-On top of the **Events** table, we’ve created one view called **Employee Events**. This provides a **Read-Only** version for the **Events** table for users with the **Employee** role, omitting the **Expense Claims** column.
-
-
-
-Within the **Expense Claims** table, we’ve created three views, called **Claims to Review**, **Submitted Claims**, and **Unsubmitted Claims**.
-
-The **Claims to Review** View is reserved for the **Manager** role for both **Read** and **Write** actions. It also uses a filtering expression to only include the rows where the **Status** attribute is equal to **Submitted**. All columns are writable except **Submitter Notes**, which is read-only.
-
-
-
-The **Submitted Claims** View provides **Read-Only** access to **Employees**, using a filtering expression to only include rows where the **Status** is **Submitted** and the **Submitter** is equal to the **Current User**.
-
-The **Unsubmitted Claims** View provides the same access for **Employees**, except the **Status** filter is set to **Not Submitted**.
-
-Our **Expense Items** table has a single View, giving **Employees** access only to the rows where the **Submitter** attribute in the related **Expense Claims** table is equal to the **Current User**.
-
-### User interfaces
-
-Our app features several distinct screens for each role. Check out our docs page on [working with screens within Budibase](https://docs.budibase.com/docs/screens).
-
-We’ve created a home page as an initial screen for all **Employee** users. This allows them to navigate to expensible events. From here, they can navigate to a form where they can start logging itemised expenses.
-
-When they click this, a new **Expense** row is created for the **Employee** to start populating with related **Items**.
-
-
-
-**Employees** can also access a a screen for viewing their previous submissions. This features two tables - one displaying all of their submitted claims and the other for claims that are currently in review.
-
-Take a look at our documentation on [working with data in design](https://docs.budibase.com/docs/working-with-data) to learn more about how this works.
-
-
-
-**App Admins** can access a dedicated CRUD screen for creating, viewing, and editing **Events**.
-
-
-
-Managers are able to view the details of submitted claims and either approve or reject them using **Row Actions**, as we’ll see in the following section.
-
-### Automations
-
-Our app relies on automations for carrying out user actions in a controlled manner, including to create, approve, cancel, and reject claims. Take a look at our [automation docs](https://docs.budibase.com/docs/automation-steps) to learn more about automating workflows in Budibase.
-
-The majority of these are created using **Row Actions**. These are user-triggerable automations that relate to a specific row in one of our tables or Views.
-
-Our **Employees** can trigger two automations. The first of these, **Submit**, triggers when a user submits their claim, setting the **Date** to the current timestamp and the **Status** to **Submitted**.
-
-
-
-The other attributes, including the linked **Expense Items,** are then populated with a **Save Row** button action on the form UI itself.
-
-**Employees** can also trigger a **Row Action** called **Cancel**. With a button push, they can update the **Status** of their claim to **Cancelled**, removing it from the review process without deleting the original record.
-
-
-
-**Managers** also use **Row Actions** to respond to claims. Depending on whether they hit **Approve** or **Reject**, the **Status** is updated to **Approved** or **Rejected**. Then, the original requestor is emailed, notifying them.
-
-
-
-## Customising our change request template
-
-Budibase templates offer fully functional solutions for key use cases. However, they’re also intended as a basis for further customisation, enabling you to quickly create apps with your own data and business logic.
-
-Let’s check out some of the key modifications you might want to make to our expense approval template.
-
-### Updating the data model
-
-The Data section is the core of the app-building experience in Budibase. As we said earlier, our application is built on top of three tables within BudibaseDB. We can modify this by either swapping in an external database or making changes to our existing one.
-
-To connect to an external database, we’d need to recreate our internal DB’s views and access rules before swapping the existing UIs and automations for your new data.
-
-Alternatively, we can alter the schema of our existing tables using the Data section’s spreadsheet-like interface.
-
-Say we wanted to add a new **Text** column to our **Expense Items** table, to store the **Vendor**, for more detailed records. We could do so using the **Plus** icon.
-
-
-
-### Adding and altering user roles
-
-User permissions for both accessing data and carrying out actions on it are also configured within the Data section. Another key way we can modify our template is by altering this, including adding or removing roles, or updating the permissions granted to existing ones.
-
-We can add entirely new roles under **Manage Roles**. Budibase offers branching inheritance for custom roles within a visual editor. So, we can create hierarchical roles, as well as ones with entirely separate permissions.
-
-For instance, we might want to create an **Auditor** role that has **Read-Only** access across all data, but no **Write** permissions for any of our tables.
-
-To achieve this, we’d create a parallel branch of permissions.
-
-
-
-We’d then need to create **Auditor** Views of our data tables and assign read-only access to all attributes under the **Columns** tab.
-
-
-
-### Adding extra screens
-
-We can also modify our template by altering its UIs. We can add or remove screens or make adjustments to the components on existing ones.
-
-Let’s carry on with our example of providing **Read-Only** access to our **Expense Claims** to our new **Auditor** role.
-
-From our **Auditor View**, we’d hit Screens to generate a table UI with side panels.
-
-
-
-Then, all we need to do is make a few tweaks to the screen that’s outputted, including removing the **Create** form and setting the **Update** form to **View**.
-
-
-
-### Adding your own business rules
-
-Budibase templates are an ideal starting point for creating custom applications to enforce your own business rules while retaining our pre-built interfaces.
-
-For example, we could provide an action to allow our auditor to flag certain submissions as suspicious.
-
-To do this, we’d start by adding a **Row Action** to our new **Auditor View**. We’ll call this **Flag Expense**.
-
-
-
-We’re then brought to the automation editor.
-
-
-
-Back in the Data section, we’ll update the **Status** column on our **Expense Claims** table, adding an option called **Flagged**.
-
-
-
-Within our **Row Action**, we’ll then use the same flow as our existing automation rules, only this time we’ll update the relevant row’s **Status** to **Flagged**.
-
-
-
-Lastly, on our new **Auditor** screen, we’ll add a **Button** to the existing **Table** component, allowing them to trigger our **Row Action**.
-
-
+* [Budibase DB](https://docs.budibase.com/docs/budibasedb)
+* [Working with screens](https://docs.budibase.com/docs/screens)
+* [Working with email automations](https://docs.budibase.com/docs/send-email)
+* [Working with data in design](https://docs.budibase.com/docs/working-with-data)
diff --git a/docs/Getting started/templates/index.md b/docs/Getting started/templates/index.md
index 468429f49..1ec0d6951 100644
--- a/docs/Getting started/templates/index.md
+++ b/docs/Getting started/templates/index.md
@@ -1,7 +1,7 @@
---
title: Templates
excerpt: ''
-deprecated: false
+deprecated: true
hidden: true
metadata:
title: ''
diff --git a/docs/Getting started/templates/risk-management.md b/docs/Getting started/templates/risk-management.md
index f0e674c52..cf9503cfb 100644
--- a/docs/Getting started/templates/risk-management.md
+++ b/docs/Getting started/templates/risk-management.md
@@ -1,7 +1,7 @@
---
title: Risk Management
excerpt: ''
-deprecated: false
+deprecated: true
hidden: false
metadata:
title: ''
@@ -10,401 +10,107 @@ metadata:
next:
description: ''
---
-Our risk management template provides a ready-to-use solution for recording, assessing, and working with data around risks, incidents, and improvement initiatives.
+Use the Risk Management template to track risks, incidents, and improvement requests through a simple submission and approval flow.
-It’s fully customizable, offering the perfect basis for creating your own custom solution utilizing your own data, logic, and processes.
+To use this template, [open it here](https://budibase.app/builder?template=app/risk-management).
-In this guide, we cover everything you need to know about working with our template, including what it does, how it’s built, and the key ways that you can modify it to suit your needs.
+## What this template includes
-To use this template, [click here](https://budibase.app/builder?template=app/risk-management).
+The template is built for three submission types:
-## What does this template do?
+* Risks
+* Incidents
+* Improvements
-At its core, our application is based around submission and approval workflows for risks, incidents, and improvements.
+It provides:
-This includes:
+* Role-based submission and review workflows
+* Status tracking for submitted records
+* Approval and rejection actions
+* Email notifications for key workflow events
+* A project table for approved work
-* Enabling **App Users** to submit new risks, incidents, or improvements. They also can view and cancel their own previous submissions.
-* Routing submissions to **Power Users** for review and approval.
-* Providing records of all ongoing and previous submissions.
-* Notifying stakeholders of changes to the status of submissions or when there are new submissions that need their attention.
+## Roles and access
-Our template utilizes simple approval workflows for each type of submission, with two distinct user roles in addition to our **App Admin** role.
+The app uses three role groups:
-It’s built on top of five tables within Budibase’s internal database. We’ll examine what each of these is and what it stores a little later.
+* `App Users` create submissions and manage their own records
+* `Power Users` review all submissions and apply approvals or rejections
+* `App Admins` manage the full dataset and app configuration
-Let’s check out the core functionality first.
+Keep role permissions aligned with the workflow. Each role should only see the records and actions it needs.
-### CRUD operations
+## Data model
-Our application enables different kinds of users to perform specific CRUD actions across the various underlying tables, with user permissions varying according to their roles.
+The template uses five internal tables plus Budibase’s `Users` table.
-**App Users** can create new records on our Improvements, Risks, or Incidents tables using dedicated data submission forms, populating only a subset of each table’s columns.
+| Table | Purpose |
+| :--- | :--- |
+| `Glossary` | Reference definitions for the template content |
+| `Risks` | Stores risk submissions |
+| `Incidents` | Stores incident submissions |
+| `Improvements` | Stores improvement submissions |
+| `Improvement Projects` | Stores accepted work and related follow-up activity |
-
+### Table relationships
-However, they have limited permissions to **Read** or **Update** entries across the tables. Specifically, they’re only able to access their own previous submissions. Within these, they can update the **Status** attribute to **Canceled** via a **Row Action**.
+The main relationships are:
-
+* `Improvement Projects` links to `Risks`
+* `Improvement Projects` links to `Incidents`
+* `Improvement Projects` links to `Improvements`
+* Each submission table links to `Users` for ownership and auditing
-This works the same way for **Improvements, Risks**, and **Incidents**.
+Use `Single User` fields for ownership, creator, or reviewer tracking, and `Relationship` fields for the links between tables.
-### Approval workflows
+## Workflow
-**Power Users** inherit all permissions from our **App Users**. On top of this, they can view all incoming submissions across our three tables.
+The submission flow is:
-They can also carry out **Update** actions relating to certain fields.
+1. A user creates a risk, incident, or improvement.
+2. The record is assigned a submission status.
+3. A power user reviews the record.
+4. The record is approved, rejected, or canceled.
+5. Email notifications are sent at the relevant step.
-Specifically, they can use **Row Actions** to either **Approve** or **Reject** the row in question, updating its status attribute. At the same time, they can use a form UI to populate the **Feedback** column**.**
+Approved items can be linked to an `Improvement Projects` record for follow-up work.
-If a submission is **Approved**, it can also be assigned to a specific **Improvement Project** for implementation by populating a relationship column.
+## Screens
-
+The template uses a mix of autogenerated and custom screens.
-### Tracking submission statuses
+* App users use submission forms and personal status views
+* Power users use review screens for approvals and rejections
+* Admins use management screens for monitoring and reporting
-Both classes of users are able to track the status of the submissions that are relevant to them.
+Keep these screens role-specific. That makes the workflow easier to scan and reduces unnecessary branching in the UI.
-Again, this matches the level of data exposure that’s provided for each Role, as we laid out earlier.
+## Automations
-So, **App Users** can access dedicated screens where they can check the status of any of their own previous submissions.
+The main automations cover:
-Meanwhile, **Power Users** are able to edit submissions, regardless of the user who created them, including viewing or updating their statuses.
+* Submission notifications
+* Approval and rejection updates
+* Cancellation handling
+* Scheduled email summaries for power users
-### Automated emails
+If you change the statuses or add a new workflow stage, update the related automations at the same time.
-Lastly, our application utilizes Budibase’s built-in email automation actions to send notifications to stakeholders based on defined events in our approval workflows.
+## Customising the template
-Specifically, the original creator of a submission across any of our three tables is notified when it has a response.
+Start by adjusting:
-In the case of a **Rejection** or **Approval**, the user is notified of this outcome. When they **Cancel** their own submissions, they’ll also receive email confirmation of this.
+1. Role names and permissions
+2. Status values
+3. Submission fields
+4. Review actions
+5. Email notification content
-**Power Users** receive a summary of the submissions that require their attention at fixed intervals via a separate automation.
+If you replace the internal tables with your own data model, update the views and automations together.
-Take a look at our documentation on [working with email automations](https://docs.budibase.com/docs/send-email).
+## Related guides
-## How does it work?
-
-Now that we know what our template does, we can drill deeper into how it is built across the different sections of the Budibase builder.
-
-### User roles
-
-Our risk management template relies on three user roles within Budibase’s RBAC system. RBAC enables us to assign a role to individual users in order to grant them access to appropriate data and actions.
-
-We’ll see how permissions are actually granted in the next section.
-
-Users with the **App User** role can primarily create submissions across our three tables. They can also view key details of their own previous submissions, including their status, or cancel the submission.
-
-**Power Users** can view all submissions, as well as triggering approval or rejection automations and assigning them to projects. They also inherit all permissions from **App Users**.
-
-Roles are configured using Budibase’s visual RBAC editor. We can use branch UIs to define how roles relate to one another, including permission inheritance.
-
-
-
-We can use this to define which actions and data each role is permitted to access from within the data section, as we’ll see in the following section.
-
-Users with the **App Admin** role have full CRUD permissions across all tables.
-
-### Data structure
-
-Our template utilizes Budibase’s internal database, enabling us to define and manage schemas using our Data section’s visual editor.
-
-Check out our documentation on [Budibase DB](https://docs.budibase.com/docs/budibasedb) to learn more.
-
-Our application relies on several interrelated internal tables, as well as role-specific **Views** based on these.
-
-Let’s break it down.
-
-#### Tables
-
-As we said earlier, our template utilizes five internal tables. These are:
-
-* **Glossary** - Storing definitions of our different kinds of submissions. This is provided as a reference for **App Admins**.
-* **Risks** - For storing submissions relating to potential risks.
-* **Incidents** - Where incidents are recorded and tracked.
-* **Improvements** - Allowing customers to suggest new improvement initiatives.
-* **Improvement Projects** - Which accepted Risks, Incidents, or Improvements can be related to.
-
-Our app also relies on Budibase’s internal **Users** table to enable individual rows across these to be assigned to specific users, as we’ll see in the next section.
-
-Our glossary simply stores definitions for each of our other tables.
-
-Each of our other tables stores data that’s required to record and process submissions. Here’s a summary of the attributes within each.
-
-**Improvement Projects:**
-
-* **title** - Text,
-* **id** - Number,
-* **created\_by** - Single User,
-* **last\_updated\_by** - Single User,
-* **description** - Long Forn Text,
-* **created\_at** - Date,
-* **status** - Single Select,
-* **start\_date** - Date,
-* **end\_date** - Date,
-* **project\_manager** - Single User,
-* **improvement** - Relationship,
-* **risks** - Relationship,
-* **incidents** - Relationship,
-* **updated\_at** - Date.
-
-**Improvements:**
-
-* **title** - Text,
-* **id** - Number,
-* **start\_date** - Date,
-* **end\_date** - Date,
-* **created\_at** - Date,
-* **updated\_at** - Date,
-* **description** - Long Form Text,
-* **created\_by** - Single User,
-* **last\_updated\_by** - Single User,
-* **Improvement Project** - Relationship,
-* **status** - Single Select.
-* **feedback -** Long Form Text.
-
-**Risks:**
-
-* **title** - Text,
-* **id** - Number,
-* **created\_at** - Date,
-* **updated\_at** - Date,
-* **created\_by** - Single User,
-* **last\_updated\_by** - Single User,
-* **description** - Long Forn Text,
-* **improvement\_project** - Relationship,
-* **status** - Single Select,
-* **feedback** - Long Form Text.
-
-**Incidents:**
-
-* **title** - Text,
-* **id** - Number,
-* **reported\_by** - Single User,
-* **employee\_involved** - Single User,
-* **date\_of\_incident** - Date,
-* **date\_created** - Date,
-* **notes** - Long Form Text,
-* **status** - Single Select,
-* **Improvement Project** - Relationship,
-* **feedback** - Long Form Text.
-
-#### Relationships
-
-Our app utilizes two distinct types of relationships.
-
-Relationships between tables in BudibaseDB are created using a special **Relationship** column. This allows us to configure links between rows across tables, either within our Data section or in end-user applications.
-
-We’ve used three instances of this to create **Many-To-One** relationships from our **Improvement Projects** table to the **Improvements**, **Risks**, and **Incidents** tables.
-
-
-
-
-
-Rows in each of our tables can also be linked to records in Budibase’s internal **Users** table. This allows us to record which user created or updated certain submissions, as well as assigning rows to particular owners.
-
-In Budbase, we have two dedicated columns for linking tables to **User** records - the **Single User** or **Multi-User** columns.
-
-Our app utilizes Single User columns across each of our tables to represent various relationships with users.
-
-
-
-#### Views
-
-**Views** are subsets of data across one or more tables, with specific rules around accessing and interacting with data.
-
-In Budibase, these are used to centrally configure what each user role can do with our stored data, including granting read or write permissions at the row or column level. We can then use these to autogenerate CRUD UIs and automation rules.
-
-Our underlying tables have their **Access** settings to only be readable or writable by **App Admins**.
-
-
-
-Each of our tables has distinct Views for each of our two core user roles.
-
-For **App Users**, the views for our **Risks, Incidents**, and **Improvement** tables are broadly similar, enabling them to create records, read their own submissions, or set the status attribute to cancel via a **Row Action**.
-
-The specific attributes that users can read or write are then defined under the **Columns** tab, so **App Users** are able to access the columns that are required to initially create their submission, check its status, or perform a cancellation.
-
-Each of these tables has two additional **Views** for **Power Users**. The first provides full **Read** and **Write** access across the tables, along with two **Row Actions** to either **Approve** or **Reject** submissions.
-
-This is attached to the respective **Review** screens for each of our submissions tables.
-
-The other provides **Read-Only** access for each of the submission tables. This populates the **Projects** screen.
-
-We can also use **Views** to perform key calculations on our database tables. Our template features views for each of our three submissions tables to provide counts of submissions for individual users based on their statuses.
-
-For instance, we have a **View** called **My Improvements Count**. Using the **Configure Calculations** tab, we’ve set this to return the **Count** of rows, **Grouped By** the **status** attribute.
-
-This will return each unique status, along with the number of relevant entries in our table.
-
-
-
-However, we don’t want to include any submissions that have either been canceled or rejected. We also only want to include the rows that are related to the current user in the front end.
-
-So, we’ve applied three filtering rules. The first includes rows that have been submitted by the current user. The other two exclude rows where the **status** is **Rejected** or **Canceled**.
-
-
-
-These are then displayed on the home screen for App Users, as we’ll see in a moment.
-
-### User interfaces
-
-Our application uses a combination of fully custom screens and UIs built around Budibase’s autogenerated layouts.
-
-Check out our docs page on [working with screens within Budibase](https://docs.budibase.com/docs/screens).
-
-The screens available for each user cohort across our three types of submissions follow a broadly similar pattern.
-
-**App Users** can access three kinds of screens. Firstly, there are three data collection forms, where they can create records on our three submission tables. These rely on Budibase’s autogenerated **Form** layout, with minor changes to their design.
-
-
-
-We’ve provided fully custom screens for **App Users** to view, track, or cancel their own submissions. These are made up of a variety of nested components wrapped in **Data Providers** and **Repeaters** to populate display elements with values from the relevant Views.
-
-Take a look at our documentation on [working with data in design](https://docs.budibase.com/docs/working-with-data) to learn more about how this works.
-
-
-
-We’ve also created a dedicated home screen for **App Users**. This provides links to navigate to the screen for each type of submission. Its design relies on stylized **Container** and **Text** components.
-
-The **Container** relating to each type of submission displays the total number of entries that the user has created, along with how the active submissions break down by status.
-
-First, we have a **Text** component that displays the total number of each type of submission by the current user, by calculating the length of the relevant **View**. So, for improvements, we use `{{ My Improvements Data Provider.Rows Length }} Submitted`
-
-The My Improvements Data Provider has a filtering expression to exclude entries where the **status** is set to **canceled**.
-
-
-
-Beneath this, we have a second **Text** component which is wrapped in a **Data Provider** and **Repeater**.
-
-These components will connect to a data source and display each of the elements nested within them for each individual row. In our case, we’re displaying the **status** and **count** from our **View Calculation**, using:
-
-```
-{{ Improvements Counts Repeater.My Improvements Counts.Count id }} {{ Improvements Counts Repeater.My Improvements Counts.status }}
-```
-
-
-
-**App Users** can also use buttons to navigate to either the submission form or table UI for each respective type of submission.
-
-Our **Power User** screens for reviewing submissions follow the same pattern as the **App User** versions we saw earlier, with the additional ability to **Approve** or **Reject** records.
-
-They can add additional details on a modal form that’s displayed when they hit reject.
-
-
-
-**Power Users** can also access a similar screen for viewing or editing **Improvement Project** records, including reading all of the details of associated submissions, via the **View** we discussed earlier.
-
-### Automations
-
-In Budibase, the Automation section is used to create workflow logic within our applications, including enabling users to take specific actions. Take a look at our [automation docs](https://docs.budibase.com/docs/automation-steps) to learn more.
-
-Our template makes extensive use of **Row Actions**. These are user-triggerable automation workflows that run on a specific row within a database table. These can be created from the Data section, under the **Row Actions** tab within a database table or view.
-
-The **App User** view for each of our submission tables features a **Row Action** called **Cancel**.
-
-Once triggered, this performs two actions. First, it updates the **Status** attribute of the trigger row to **Cancelled**. Then, it sends a template email to the user, confirming that this has been successful.
-
-
-
-**Power Users** have two additional **Row Actions** for each submission table, called **Approve** and **Reject**. These follow the same structure as our **Cancel** action, changing the **Status** to **Approved** and **Rejected**, respectively.
-
-They then send an email template to the original submitter, notifying them of the outcome.
-
-
-
-Lastly, we’ve built an Automation called **Outstanding Attributes**, using a **Chron Trigger**. This enables us to set rules which are executed at defined intervals. Ours runs every Monday and Thursday morning at 09:30.
-
-We’ve set our Cron Trigger manually, but we also have the option of using AI to generate an expression.
-
-
-
-This uses three **Query Rows** steps to return all of the rows from our **Risks**, **Incidents**, and **Improvements** tables with **Status** attributes set to **Pending**.
-
-We then use a **Condition** block, with some custom JavaScript, to count the total number of rows across all three tables that are currently **Pending**. If this is **not 0**, we continue.
-
-Lastly, we use an **Email** action to notify relevant users that submissions need their attention. By default, this is set to notify the current user, but this can be overridden to target a specific email address or a comma-separated set of emails.
-
-Alternatively, we could configure this to notify all users with a particular role, such as **Power User** or **App Admin**.
-
-By default, this Automation is set to **Paused**.
-
-## Customizing our risk management template
-
-Budibase templates are created as ready-to-use solutions, but they also provide a basis for you to build your custom workflow apps.
-
-Let’s think about some of the key ways that you can modify our change management template within Budibase.
-
-### Updating the data model
-
-The Data section is the core of every Budibase app. Our template utilizes Budibase’s internal database. So, we have two broad options for customizing our data model. We could swap this for an entirely different database, or we could alter the schema of our existing one.
-
-If we wanted to connect to an external SQL database, for example, we could recreate the various Views that we have in place for our existing database before connecting this to our existing UIs and automations.
-
-We can also easily modify the schema of our existing database, including adding, removing, or editing existing tables and columns, using the spreadsheet-like interface in Budibase’s Data section.
-
-For example, say we wanted to add a new Long Form Text column to our **Risks** table, called inspector\_notes.
-
-We could do this by hitting the **+** icon at the top of our table.
-
-
-
-At the same time, we might update our existing **status** attribute by adding an option called **Inspected**.
-
-
-
-### Adding and altering user roles
-
-Another key area where you can modify our template is its user roles. Budibase offers a fully visual interface for creating and configuring user roles, including with branching inheritance.
-
-We’re going to create a new role called **Inspector** this will inherit permissions from the existing **App User** role, but it will be placed in its own branch, separate from the **Power User** role.
-
-This means that **Inspectors** will be able to create any submissions that **App Users** can, as well as whatever additional permissions we grant them. However, this will be unrelated to the existing **Power User** role’s permissions.
-
-
-
-To go along with this, we’ll create a new **View** and set its **Access** to the **Inspector** role. We’ll then make all columns except for **Inspector Notes** read-only.
-
-
-
-We’ll also set a filter to only include entries where the **status** is set to **Pending**.
-
-
-
-Now, **Inspectors** have permission to view all pending **Risks** entries, and add their notes.
-
-### Adding extra screens
-
-Next, we need to provide an interface for our inspection workflow.
-
-This will be relatively simple, enabling **Inspectors** to read our entries and populate the **Inspector Notes** field.
-
-From our new view, we’ll start by autogenerating a **Table** screen with the **Side-Panel Form** option.
-
-
-
-Then, within the Design section, we’ll make a couple of UX tweaks. Specifically, we want to remove the **Create Form**. On our remaining **Edit** form, we’ll remove the **Delete** button and set all fields except for **Inspector Notes** to **Disabled**.
-
-
-
-### Adding your own business rules
-
-Lastly, we can use the Automation section to add our own application logic. To carry on our example we want to add an automation flow so that when the **Inspection Notes** field is populated, the **status** is updated to **Inspected**.
-
-We’ll then email the original submitted, notifying them of this update.
-
-We’ll start by creating a **Row Action** from our new database View. We’ll call this **Inspect**.
-
-
-
-We’ll then mimic the structure of our existing **Row Actions**, first adding an **Update Row** step, followed by an **Email** step.
-
-To update our row, we’ll set the **ID** to our trigger ID and the **status** to **Inspected**. We’ll populate the **Send To, Subject**, and **Body** fields of our **Email** action too.
-
-
-
-Next, we need to provide a way to trigger this automation from our app’s UI. Head back to the Design section, and we’ll add a **Button Action** to the form on our **Inspection** screen.
-
-Here, we’ll add a **Row Action** step. Our **Form Block** has an attribute called **RowID**, which we’ll copy into the corresponding field on our button action.
-
-
+* [Budibase DB](https://docs.budibase.com/docs/budibasedb)
+* [Working with screens](https://docs.budibase.com/docs/screens)
+* [Working with email automations](https://docs.budibase.com/docs/send-email)
+* [Working with data in design](https://docs.budibase.com/docs/working-with-data)
diff --git a/docs/References/budibase-architecture.md b/docs/References/budibase-architecture.md
index 6b03a72ec..15ef07c4b 100644
--- a/docs/References/budibase-architecture.md
+++ b/docs/References/budibase-architecture.md
@@ -10,33 +10,32 @@ metadata:
next:
description: ''
---
-The Budibase architecture comprises of a few key Budibase services and a collection of open source technologies that Budibase is built on top of.
+Budibase architecture is made up of a few core services and the open source technologies they depend on.
-## Hosted architecture
+## Overview
-In this section, we will cover the core services that make up the Budibase self-hosted platform and how this architecture fits together.
+Budibase runs as a small set of services behind a single proxy.
-1. [App service](https://github.com/Budibase/budibase/tree/master/packages/server) - this is the core of the Budibase infrastructure, the apps service hosts your web apps when they have been deployed as well as providing the REST API that your web apps will use to access/store their data.
-2. [Worker service](https://github.com/Budibase/budibase/tree/master/packages/worker) - a small custom Budibase service which currently handles platform security, making sure users attempting to deploy have permission to do so.
-3. [Database (CouchDB)](https://couchdb.apache.org) - this is the primary database of Budibase, it is a NoSQL highly scalable and reliable platform which supports simple [data replication](https://docs.couchdb.org/en/stable/replication/protocol.html) and [partitioning](https://docs.couchdb.org/en/stable/partitioned-dbs/index.html). We utilise data replication when deploying apps as well as it being particularly useful for synchronising a cluster for large scale deployments. We partition the CouchDB service into databases specific to each app, meaning we can keep data separated easily and securely.
-4. [Object store (MinIO)](https://min.io) - it is possible to store large files in Budibase apps, as well as the apps being built on top of a specific version of the Budibase Client (the Svelte app at the core of every Budibase web app). To store these files we make use of MinIO, which works similarly to AWS S3.
-5. [Proxy (NGINX)](https://nginx.com) - To simplify the deployment and usage of the Budibase platform we route all requests through a proxy engine which sits at the front of the Budibase architecture. No service is communicated with directly, this also allows us to load balance the services as the system grows if necessary.
-6. [Cache (Redis)](https://redis.io/) - We utilise Redis as a cache store, for information that is commonly accessed such as app metadata. We also store some information about active user sessions in Redis, as these are required with every user request to confirm their identity/authentication.
+The proxy is the public entry point. It routes traffic to the app and worker services, while CouchDB stores app data, MinIO stores files, Redis stores shared metadata and sessions, and LiteLLM powers Budibase AI and agent features. LiteLLM uses Postgres for its own metadata.
-This architecture will look the same no matter the method of self hosting, all of the above services are required for Budibase to function.
+The exact container layout differs between deployment methods, but these logical services are part of the default self-hosted platform.
-## Optional architecture
+## Core services
-Some services are not included in all installations - they are optional, they may only provide a service which is useful to that particular environment.
-
-1. [Updates (Watchtower)](https://containrrr.dev/watchtower/) - For self hosted docker-compose updates Watchtower can be used. This image is only provided in our docker-compose setup and is entirely optional, if you plan to update using the Budibase CLI, or manually this container can be removed. It can be used to perform updates to the Budibase images (app and worker services) via the UI, however if this method is used we recommend checking periodically whether or not your docker-compose scripts require updates as well; watchtower can only update image versions.
+1. [App service](https://github.com/Budibase/budibase/tree/master/packages/server) - hosts deployed web apps and provides the REST API used to access and store data.
+2. [Worker service](https://github.com/Budibase/budibase/tree/master/packages/worker) - handles platform security and checks whether deployment requests are allowed.
+3. [Database (CouchDB)](https://couchdb.apache.org) - stores app data and supports [replication](https://docs.couchdb.org/en/stable/replication/protocol.html) and [partitioning](https://docs.couchdb.org/en/stable/partitioned-dbs/index.html). Budibase uses separate databases per app to keep data isolated.
+4. [Object store (MinIO)](https://min.io) - stores uploaded files and other large assets. It works similarly to AWS S3.
+5. [Cache (Redis)](https://redis.io/) - stores commonly accessed metadata and active session data.
+6. [AI service (LiteLLM)](https://docs.litellm.ai/) - provides the proxy used for Budibase AI and agent features.
+7. [AI database (Postgres)](https://www.postgresql.org/) - stores LiteLLM metadata when Budibase AI is enabled.
## Web app architecture
-Lastly the most important component of Budibase, the apps you make! Each Budibase app is made up of a few core libraries:
+Each Budibase app is made up of a few core libraries:
-1. [Svelte](https://svelte.dev) - Budibase loves Svelte, and all our web apps at their core are built with Svelte.
-2. [Client library](https://github.com/Budibase/budibase/tree/master/packages/client) - as with the builder each Budibase app has a version of the client library that it was built with, this provides the interactivity and data connections you need for your app to function.
-3. [Component library](https://github.com/Budibase/bbui) - we want to make it easy to build consistent web apps quickly; for this reason, we provide a core set of components that apps can be built out of - this is included in your app.
+1. [Svelte](https://svelte.dev) - the core framework used by Budibase web apps.
+2. [Client library](https://github.com/Budibase/budibase/tree/master/packages/client) - provides the interactivity and data connections your app needs.
+3. [Component library](https://github.com/Budibase/bbui) - provides the reusable components used to build apps consistently.
-Hopefully, this overview has explained how the Budibase platform works, from the builder to your deployed web apps.
+This overview shows how the Budibase platform fits together, from the builder to deployed web apps.
diff --git a/docs/References/command-interface.md b/docs/References/command-interface.md
index 8be92de0d..96bec710e 100644
--- a/docs/References/command-interface.md
+++ b/docs/References/command-interface.md
@@ -71,7 +71,7 @@ This shows a list of all tables you have fetched within an app. Allows you to ea
## Query
-Find all [queries](doc:postgresql#better-approach-using-a-custom-query) related to data sources here. Navigate to view and manage these queries.
+Find all [queries](doc:sql-workflow#custom-queries) related to data sources here. Navigate to view and manage these queries.
* **Query**: Create Row
* **Query**: Get User Record
@@ -97,4 +97,4 @@ Change the appearance of the builder interface by selecting a [theme](doc:app-th
* **Change Builder Theme**: Dark
* **Change Builder Theme**: Darkest
* **Change Builder Theme**: Nord
-* **Change Builder Theme**: Midnight
\ No newline at end of file
+* **Change Builder Theme**: Midnight
diff --git a/docs/References/deployment-information.md b/docs/References/deployment-information.md
index 8d8762843..805d77e30 100644
--- a/docs/References/deployment-information.md
+++ b/docs/References/deployment-information.md
@@ -10,21 +10,22 @@ metadata:
next:
description: ''
---
-A common question asked about deployment is how does it work and where does it go - an important question to ask before considering a platform to build your tools on top of.
+Deployment questions usually come down to two things: where the app goes and what gets deployed.
## Where deployments go
### Self-hosted
-In the case of a self-hosted deployment, the answer is very simple, the app, attachments and all data will be uploaded to the self-hosted platform, this data will be spread between your MinIO and CouchDB services, being served by the Budibase apps service.
+For a self-hosted deployment, the app, attachments, and data are uploaded to your own platform. They are stored across MinIO and CouchDB and served by the Budibase apps service.
## How does deployment work
-Another important question is the technical side of what actually happens when your app is deployed. Here I'll give a quick overview of the steps taken by the Budibase builder and platform to get your app live! This process is nearly identical whether you're deploying to a self-hosted service or if you're deploying to the Budibase Cloud; the only differences being around how Budibase keeps everyone's apps separate and within user quota limits in the Cloud.
+The deployment process is mostly the same whether you deploy to self-hosted infrastructure or Budibase Cloud. The main differences are how apps are separated and how Cloud quotas are enforced.
-1. First the builder sends up your hosting/API key to confirm you have access to the platform, if accepted the platform will return tokens to allow for a one-time deployment to the various services
-2. Next the builder uses these tokens to talk through our proxy service to the database and object store engines, storing app data, metadata about the app and the Budibase client, the Svelte app that is the base of every Budibase app.
-3. All metadata and app data is stored in [CouchDB](https://couchdb.apache.org), a NoSQL database that importantly supports [replication](https://docs.couchdb.org/en/stable/replication/protocol.html). We replicate the data from your builder to the CouchDB service, meaning we can merge data from existing apps with any updates you've made in the builder as well as deploying fresh apps.
-4. Lastly the builder confirms if the deployment was successful and returns the information about how to access the app to the user. This will also include working our the URLs for any webhooks which are now running in the deployed app.
+1. The builder sends your hosting or API key to confirm access.
+2. If the request is accepted, the platform returns deployment tokens.
+3. The builder uses those tokens to write app data, metadata, and the Budibase client through the proxy service.
+4. App and metadata storage live in [CouchDB](https://couchdb.apache.org), which supports [replication](https://docs.couchdb.org/en/stable/replication/protocol.html). Budibase replicates data from the builder to CouchDB so updates can be merged with existing apps.
+5. The builder confirms the deployment and returns access details, including webhook URLs for the deployed app.
-As you can see the deployment process is quite simple, secure, upload, and then confirm, then you're ready to start using the app!
\ No newline at end of file
+The process is simple: authenticate, upload, confirm, and then use the deployed app.
diff --git a/docs/References/feature-flags.md b/docs/References/feature-flags.md
index 366514434..269113088 100644
--- a/docs/References/feature-flags.md
+++ b/docs/References/feature-flags.md
@@ -7,10 +7,10 @@ metadata:
description: kba_5hjpe
robots: index
---
-We release features behind feature flags, before they are generally available.
+We release some features behind feature flags before they are generally available.
- Flagged features are still under development. We cannot guarantee their stability and they are subject to change. We recommend that you use them in a test environment. Proceed at your own risk!
+ Flagged features are still under development. They may change and are not guaranteed to be stable. Use them in a test environment.
## Enabling a feature flag on self-host
@@ -31,11 +31,11 @@ Copy this line exactly. This will pass the value from the `.env` file, into your
## Enabling a feature flag on Budibase Cloud
-We will need to enable this from our end. Contact [support@budibase.com](mailto:support@budibase.com). Make sure to include
+We need to enable this from our side. Contact [support@budibase.com](mailto:support@budibase.com) and include:
* The name of the feature flag that you'd like enabled.
* The URL of your Budibase tenant.
-We cannot guarantee that we will enable the feature on your account - we will consider on a case-by-case basis.
+We cannot guarantee that we will enable the feature on your account. We review requests case by case.
-
\ No newline at end of file
+
diff --git a/docs/References/troubleshooting-sql.md b/docs/References/troubleshooting-sql.md
index 0498b169a..9b15c923d 100644
--- a/docs/References/troubleshooting-sql.md
+++ b/docs/References/troubleshooting-sql.md
@@ -10,11 +10,11 @@ metadata:
next:
description: ''
---
-Within this section we will discuss how to troubleshoot issues with your SQL database - this will likely be with the help of a member of the Budibase team. If you are experiencing an issue one of the best ways to resolve it is to raise an [issue on Github](https://github.com/Budibase/budibase/issues/new?assignees=\&labels=bug\&projects=\&template=bug_report.md\&title=).
+This section covers common SQL troubleshooting steps. If you need help, raise an [issue on GitHub](https://github.com/Budibase/budibase/issues/new?assignees=\&labels=bug\&projects=\&template=bug_report.md\&title=).
## Minimum SQL versions
-Budibase has some core requirements, specifically for searching, that it cannot operate without. For Budibase versions 2.31+ you must be using a minimum version of:
+Budibase has minimum database version requirements for search and related features. For Budibase 2.31 and later, use at least:
1. SQL Server - SQL Server 2016 and onwards
2. MySQL - MySQL 8.0 and onwards
@@ -22,9 +22,9 @@ Budibase has some core requirements, specifically for searching, that it cannot
4. Oracle - Oracle Database 19c and onwards
5. Postgres - Postgres 9.4 and onwards
-This is primarily for aggregate functions and Common Table Expressions (CTE) which are required for Budibase to function.
+These requirements are mainly for aggregate functions and Common Table Expressions (CTEs).
-Other dialect "compatible" databases may work, but we cannot guarantee support for these in future versions of Budibase. If you need guaranteed support, please reach out to [sales](https://budibase.com/contact/) to discuss options.
+Other compatible databases may work, but we cannot guarantee future support. If you need guaranteed support, contact [sales](https://budibase.com/contact/).
## Log SQL queries
@@ -32,34 +32,6 @@ Other dialect "compatible" databases may work, but we cannot guarantee support f
>
> This feature is designed for investigating issues with Budibase accessing data in your SQL database, we recommend only enabling this when needed and disabling in production environments.
-It is possible to log the queries that Budibase is sending to your SQL database, to investigate issues or performance problems you're encountering when accessing a database. This can only be enabled in self host environments. To enable this set the environment variable `SQL_LOGGING_ENABLE` to `1` on your app services (docker-compose or Kubernetes) or if using the single image simply supply this environment variable to the container. Once you have restarted the server/container you will see SQL queries being logged in a format like:
+You can log the queries Budibase sends to your SQL database to investigate issues or performance problems. This is only available in self-hosted environments. Set `SQL_LOGGING_ENABLE=1` on your app services or on the container if you are using the single image. After restarting, queries are logged in a format like:
`[SQL] [PG] query="select * from table limit $1" values="100"`
-
-## SQL Schema export
-
-> 📘 Schema export is designed for debugging
->
-> This feature is designed to help the Budibase team resolve issues with your schema - we want to provide the best experience possible for every user out of the box and providing your schema helps to debug issues with typing, relations and other unique scenarios.
-
-In this article we will cover how to export the schema of your database through Budibase - this can be done manually with tools like `pg_dump` for Postgres, but we have provided a tool within Budibase to export what is most helpful when debugging issues. We would also like to highlight that this export is *only* the structure and schema of your tables and does not contain any data; however if you have any sensitive table or column names please discuss this with the Budibase team.
-
-It is worth noting while the export is a good representation of your data, it is not designed to be used to replicate your data within a production environment, we have designed this to extract the information needed for debugging, not a perfect mirror of the database.
-
-## Exporting your schema
-
-This feature is currently available exclusively for self-hosted instances of Budibase. It is unlikely that this capability will be added to cloud instances of Budibase.
-
-To start exporting your schema you will need to click into your database that you are experiencing an issue with. To start, click into the datasource from the "Data" section of the app builder.
-
-
-
-Within a datasource you will be able to look at its settings, tables, relationships and queries. Within these options you can select the "Settings" tab of your datasource - this will only be available SQL based datasources.
-
-
-
-From here clicking the download button will trigger a schema export from your database. This will export all of the tables that can be accessed, not necessarily just the ones that are imported into Budibase.
-
-From here, you will have a file named like `PostgresSQL-dump.sql` - you can open this file see the schema of your tables depicted.
-
-As above, this does contain all of the tables/columns within your database, we have not edited this in anyway as it could inhibit our ability to debug/discuss with you about your problem. If you do not wish to provide your schema publicly as say part of a Github issue, you can email the schema to [support@budibase.com](mailto:support@budibase.com) including a reference to the issue/discussion that this is related to.
\ No newline at end of file
diff --git a/docs/Self-hosting/hosting-methods/azure-container-instances.md b/docs/Self-hosting/hosting-methods/azure-container-instances.md
index a366c7718..da89c1743 100644
--- a/docs/Self-hosting/hosting-methods/azure-container-instances.md
+++ b/docs/Self-hosting/hosting-methods/azure-container-instances.md
@@ -10,7 +10,7 @@ metadata:
next:
description: ''
---
-Azure supports several container runtimes including AAS, ACA and ACI. In this guide we will run Budibase on the ACI service persisting data to an Azure Storage Account File Share. The File Share will be a CIFS file share as NFS file shares are not yet supported on Azure.
+Azure supports several container runtimes, including AAS, ACA, and ACI. This guide runs Budibase on ACI and stores data on an Azure Storage Account file share. Azure does not yet support NFS file shares here, so this example uses CIFS.
Set some environment variables:
@@ -68,7 +68,3 @@ az container create
```
If you receive any errors you may want to try substituting the forward slash for a backslash i.e. `/home` becomes `\home`
-
-
-
-
diff --git a/docs/Self-hosting/hosting-methods/google-cloud-run.md b/docs/Self-hosting/hosting-methods/google-cloud-run.md
index 0ed562ea2..cf0599e37 100644
--- a/docs/Self-hosting/hosting-methods/google-cloud-run.md
+++ b/docs/Self-hosting/hosting-methods/google-cloud-run.md
@@ -10,7 +10,7 @@ metadata:
next:
description: ''
---
-[Cloud Run](https://console.cloud.google.com/run) is Google’s managed container service and in this guide we will install Budibase with persistent storage using [Google File Store](https://cloud.google.com/filestore). We will also install using the gcloud cli tool but the console UI could be used as an alternative.
+[Cloud Run](https://console.cloud.google.com/run) is Google’s managed container service. This guide installs Budibase with persistent storage using [Google File Store](https://cloud.google.com/filestore) and the `gcloud` CLI.
### Set Global Variables
@@ -54,11 +54,11 @@ Next we set the zone for our file store:
gcloud config set filestore/zone $ZONE
```
-At this point you may wish to check that billing is enabled for the new project.
+Check that billing is enabled for the new project.
### Create the File Store and File Share
-The command below will create an NFS file store instance. You may change the storage tier to reduce latency and suit the needs of your audience ([details](https://cloud.google.com/filestore/docs/service-tiers)).
+Create an NFS file store instance. You can change the storage tier to suit your latency and capacity needs. See the [service tiers](https://cloud.google.com/filestore/docs/service-tiers) for details.
```
gcloud beta filestore instances create $PROJECT_NAME \
@@ -67,17 +67,9 @@ gcloud beta filestore instances create $PROJECT_NAME \
--network=name="default"
```
-During creation of the file store you may be prompted to enable the file api on your project as shown below:
+During creation, you may be prompted to enable the File API on your project.
-
-
-After some time you will get confirmation that the file store instance has been created:
-
-
-
-The file store configuration may also be viewed in the UI:
-
-
+Wait for the file store instance to finish creating before continuing.
Next we want to store the IP address of the file store instance to a variable:
@@ -91,8 +83,6 @@ Then create a VPC connector:
gcloud compute networks vpc-access connectors create $PROJECT_NAME --project $PROJECT_NAME --region $REGION --range "10.8.0.0/28"
```
-
-
And next create a service account:
```
@@ -101,7 +91,7 @@ gcloud iam service-accounts create $PROJECT_NAME --project $PROJECT_NAME
### Container Registry
-With the file storage configured our next step is to make the Budibase container image available in a registry accessible to the Cloud Run service. Fetch the docker hub image with:
+With file storage configured, make the Budibase container image available in a registry accessible to Cloud Run. Fetch the Docker Hub image with:
```
docker pull --platform=linux/amd64 budibase/budibase
@@ -113,7 +103,7 @@ Then tag that image with the path to GCR for your project:
docker tag budibase/budibase gcr.io/$PROJECT_NAME/budibase/budibase:latest
```
-Next we want to instruct docker to use gcloud for autheticating to GCR :
+Next, configure Docker to use `gcloud` for authenticating to GCR:
```
gcloud auth configure-docker
@@ -127,7 +117,7 @@ docker push gcr.io/$PROJECT_NAME/budibase/budibase:latest
### Run the Container
-With the image available in GCR we can now run the container service. Notice that we are passing in environment variables for the NFS file share IP address and share name.
+With the image available in GCR, deploy the container service. Pass the NFS file share IP address and share name as environment variables.
```
gcloud beta run deploy $PROJECT_NAME --image gcr.io/$PROJECT_NAME/budibase/budibase:latest \
@@ -142,11 +132,7 @@ gcloud beta run deploy $PROJECT_NAME --image gcr.io/$PROJECT_NAME/budibase/budib
--update-env-vars FILESHARE_IP=$FILESTORE_IP_ADDRESS,FILESHARE_NAME=$FILESHARE_NAME
```
-
-
-Allow a few minutes for the service to initialise then visit the Service URL as output by the command above or as found on the UI.
-
-
+Allow a few minutes for the service to initialise, then visit the service URL shown by the command or in the Cloud Run console.
### Cleaning Up
@@ -164,6 +150,4 @@ gcloud projects delete $PROJECT_ID
### Troubleshooting
-The 'Cloud Run' service **Logs** tab can be used to look out for errors while mounting the file storage or running the app.
-
-
\ No newline at end of file
+Use the Cloud Run **Logs** tab to look for errors while mounting the file storage or running the app.
diff --git a/docs/Self-hosting/hosting-methods/linode.md b/docs/Self-hosting/hosting-methods/linode.md
index 7bd0c1eba..813944985 100644
--- a/docs/Self-hosting/hosting-methods/linode.md
+++ b/docs/Self-hosting/hosting-methods/linode.md
@@ -13,82 +13,55 @@ next:
---
### Launch Marketplace App
-After logging in to your Linode account choose [Marketplace](https://cloud.linode.com/linodes/create?type=One-Click) from the menu.
+After logging in to your Linode account, choose [Marketplace](https://cloud.linode.com/linodes/create?type=One-Click) from the menu and select the Budibase app.
-Select the Budibase app as shown below:
-
-
-
-Scroll down the page to set options for the Budibase app including:
+Set the Budibase app options:
* **Budibase Port**: The default port is 80 but you can choose another port number if you plan to add a load balancer or proxy in front of your installation
* **Select an Image**: The base operating system you would like Budibase installed on. Choose from Debian 11 or Ubuntu 22.04
* **Region**: Choose to launch the app in a region close to your intended audience
-
-
Choose a plan to match your requirements and budget:
+Provide a **Linode Label** such as the domain name you plan to use, set a **Root Password**, and add your public **SSH Key** if you want SSH access.
-
-
-Under **Linode Label** provide a useful reference such as the domain name you will use and enter a **Root Password** that can be used if you need to manage your server instance over SSH
-
-
-
-Add your public **SSH Key** that can be used if you need to manage your server instance over SSH. There are many guides and videos online demonstrating how to generate SSH keys for your own desktop operating system.
-
-
+The remaining choices are optional. Consider enabling **Backups** if you do not have another backup strategy.
-The remaining choices are optional. You may wish to choose to add **Backups** unless you have an alternative backup strategy.
+When the options are set, click **Create Linode**.
-
-
-With all the options set, click the '**Create Linode**' button at the bottom of the page:
-
-
-
-You will see a summary of your Linode server instance and a status indicating that it is currently provisioning. Please allow a few minutes for the provisioning of the server to complete.
+You will see a summary of the server and a status showing that it is provisioning. Wait for provisioning to complete.
Note that the summary shows:
-* **IP Addresses** of your server ( 139.162.155.223 in the example below ). We will use this IP address in a web browser to display Budibase but if you have already purchased a domain name this would be a good time to use the DNS settings of your domain name provider to add a DNS A record pointing to the IP address you have been provided by Linode.
-* **SSH Access** command that you can use from your computer’s command-line terminal to connect to the server
-
-
+* **IP Addresses** of your server. Use this IP address in a browser to access Budibase. If you already have a domain name, add a DNS A record that points to this IP address.
+* **SSH Access** command you can use from a terminal to connect to the server.
-When the status appears as **Running** please allow a further few minutes for the Budibase installation to complete.
+When the status is **Running**, wait a few more minutes for the Budibase installation to complete.
-
+Then open the IP address or domain in a browser to create the initial admin user.
-When the server status is shown as **Running** you can visit the IP address (or the domain name if you created a DNS entry) in a web browser to create the initial admin user:
-
-
-
-Next login as the admin user to start creating Budibase applications. There are many templates to help you get started and you can learn more about connecting to existing database and building your own custom apps from the Budibase Docs.
+Sign in as the admin user to start creating Budibase applications.
### Further Configuration
-If you would like to examine the installation you can SSH to the server using the IP address supplied by Linode and the SSH key you provided during the setup. Running `docker ps` should display the list of docker containers that run Budibase:
-
-
+If you want to inspect the installation, SSH to the server using the IP address and SSH key you provided during setup. Running `docker ps` shows the Budibase containers.
-You may wish to examine the files under `/opt/budibase`
+You may wish to examine the files under `/opt/budibase`.
-* `docker-compose.yml` Use this file for future management/customisation of your budibase install
-* `.env `The .env file contains the passwords used to access various budibase components.
+* `docker-compose.yml` Use this file for future management or customisation of your Budibase install.
+* `.env` The `.env` file contains the passwords used to access various Budibase components.
### SMTP
-By default Linode will block connections to external SMTP ports to reduce abuse by spammers. You will need to open a support ticket with Linode to unblock your Linode server if you want to send emails via an SMTP server.
+By default, Linode blocks connections to external SMTP ports. Open a support ticket with Linode if you need SMTP access.
### Backups
-Backing up your data is beyond the scope of this introduction but you should investigate storing regular copies of your docker volumes stored under `/var/lib/docker/volumes`
+Backing up your data is beyond the scope of this introduction, but you should store regular copies of your Docker volumes under `/var/lib/docker/volumes`.
### Security
-Server security is beyond the scope of this introduction but you should investigate methods to secure your server by regularly applying operating system updates with: `apt update && apt upgrade` You should also add a firewall and intrusion prevention utilities such as fail2ban.
+Server security is beyond the scope of this introduction, but you should regularly apply operating system updates with `apt update && apt upgrade`. You should also add a firewall and intrusion prevention tools such as fail2ban.
### HTTPS
-Although beyond the scope of this introduction there are a variety of ways to add a certificate to your installation including using a Linode NodeBalancer or Cloudflare. There is also the possibility of using Let’s Encrypt certbot on the server and configuring an nginx proxy listening on port 443 to proxy traffic to your Budibase container.
\ No newline at end of file
+Although beyond the scope of this introduction, there are several ways to add HTTPS, including Linode NodeBalancer, Cloudflare, or Let’s Encrypt with an NGINX proxy on port 443.
diff --git a/docs/Self-hosting/migrations.md b/docs/Self-hosting/migrations.md
index 7e938f8e4..d209fbd9b 100644
--- a/docs/Self-hosting/migrations.md
+++ b/docs/Self-hosting/migrations.md
@@ -10,19 +10,15 @@ metadata:
next:
description: ''
---
-Occasionally as part of a Budibase update we will need to make an update to the infrastructure which is used by the platform - in self host this can impact you.
+Budibase updates sometimes require infrastructure changes. In self-hosted installations, those changes can affect your deployment.
-Budibase will warn you that your installation is not up to date/is missing components of the infrastructure, this warning will look like:
-
-
- Your installation requires an upgrade
-
+Budibase will warn you if your installation is missing required infrastructure components.
Below is a list of migrations which may be needed for your installation.
## Budibase DB SQL Support
-If you have reached this page then you have been alerted to an issue with your self-host installation. As of version 2.33.0, Budibase relies on a service called the Structured Query Server ([SQS](https://neighbourhood.ie/products-and-services/structured-query-server)). This runs alongside CouchDB to provide relational searching functionality.
+If you have reached this page, your self-hosted installation needs an update. As of version 2.33.0, Budibase relies on the Structured Query Server ([SQS](https://neighbourhood.ie/products-and-services/structured-query-server)) running alongside CouchDB to provide relational search.
Below is what's required for each installation method to get SQS running.
@@ -32,9 +28,9 @@ If you are using any of the [single image/docker install methods](/docs/docker)
### `docker-compose`
-You need to update your `docker-compose.yaml` file - the quickest way to do this is using the [Budibase CLI](/docs/updating-budibase#budibase-cli-docker-compose). If you would prefer to do this manually then you will need to make sure your CouchDB installation is using the latest version of the `budibase/couchdb` image. This image runs SQS for you.
+Update your `docker-compose.yaml` file. The quickest way is to use the [Budibase CLI](/docs/updating-budibase#budibase-cli-docker-compose). If you update manually, make sure your CouchDB service uses the latest `budibase/couchdb` image, which includes SQS.
-You will also need to set a new environment variable called `TARGETBUILD`. Here's how your `couchdb-service` block in your `docker-compose.yaml` file should look:
+You also need to set the `TARGETBUILD` environment variable. Your `couchdb-service` block should look like this:
```yaml docker-compose.yaml
couchdb-service:
@@ -50,9 +46,9 @@ couchdb-service:
### Kubernetes
-You will need to make sure you are using the latest version of the Budibase Helm chart alongside the latest version of the Budibase images. **If your upgrade workflow for Kubernetes involves just updating image tags in your`values.yaml` file, this will break your installation.**
+Make sure you are using the latest version of the Budibase Helm chart and the latest Budibase images. Updating only the image tags in `values.yaml` will break the installation.
-If you followed the steps in [Kubernetes](doc:kubernetes-k8s), you'll need to run the following commands to update your Helm chart.
+If you followed the steps in [Kubernetes](doc:kubernetes-k8s), run the following command to update your Helm chart.
```shell
helm upgrade -n budibase budibase oci://ghcr.io/budibase/charts/budibase
@@ -60,4 +56,4 @@ helm upgrade -n budibase budibase oci://ghcr.io/budibase/charts/budibase
***
-If you are still having issues and have confirmed that your installation is running the latest `budibase/couchdb` image for your CouchDB service then please reach out to Budibase support for further help, in the meantime you can downgrade to the previously used version safely.
\ No newline at end of file
+If you are still having issues after confirming that your CouchDB service uses the latest `budibase/couchdb` image, contact Budibase support. You can safely downgrade to the previous version in the meantime.
diff --git a/docs/Self-hosting/reverse-proxy.md b/docs/Self-hosting/reverse-proxy.md
index 65e9a5cb1..c05f90ee9 100644
--- a/docs/Self-hosting/reverse-proxy.md
+++ b/docs/Self-hosting/reverse-proxy.md
@@ -17,65 +17,55 @@ next:
slug: accessing-minio
title: Accessing MinIO
---
-One of the main things you will want to set up before putting your Budibase platform into production use is a proxy, which can control access to the cluster via a domain (removing the need for a port number and so on) as well as allow the use of HTTPS for a domain that you own. In this section, we will provide some configuration options to get this up and running easily with [NGINX](https://www.nginx.com/) or [Caddy](https://caddyserver.com/docs/automatic-https).
+Use a reverse proxy to expose Budibase behind a domain name and enable HTTPS.
-{`
-
-`}
+This page shows the key configuration points for `Caddy` and `NGINX`.
-***
+## Before you start
-## Caddy
+Make sure you have:
-Caddy is an open-source web server with automatic HTTPS written in Go. If you want to get HTTPS up and running on your Budibase instance as quickly as possible with caddy, go through the following steps.
+* Budibase running on the target host
+* A domain name you control
+* A reverse proxy installed or available to install
-### Install Caddy
+## Caddy
-Follow the [Caddy installation instructions](https://caddyserver.com/docs/install).
-Once you have done this, you should be able to check that you have Caddy available on your machine, using the following command.
+Caddy is the quickest option if you want automatic HTTPS.
-```
-caddy version
-```
+1. Install Caddy.
+2. Create a `Caddyfile` for your domain.
+3. Add a reverse proxy rule that points to the Budibase service.
+4. Start or restart Caddy.
-Next, [create a Caddyfile](https://caddyserver.com/docs/quick-starts/caddyfile) with your domain in it - basically, create a text file named Caddyfile (no extension), then add your domain to the file.
+Example:
-```
+```text
yourdomain.com
reverse_proxy localhost:10000
```
-To set up HTTPS on your domain, you can simply restart the caddy server:
-
-```
-caddy stop
-caddy start
-```
-
-That's it! Full HTTPS and reverse proxy setup with Caddy and Budibase. Visit your domain and you will see your newly secured budibase instance with a custom domain.
-
-***
+Use this when you want a simple path to HTTPS without managing certificates manually.
## NGINX
-Please note, this section assumes some knowledge of NGINX installation, setup, and configuration. Information about NGINX can be found here:
-
-* [installation](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/)
-* [setup](https://docs.nginx.com/nginx/admin-guide/basic-functionality/runtime-control/)
-* [configuration](https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/)
+NGINX is the better fit when Budibase is one service behind a larger proxy setup.
-Our recommendation for running Budibase is a "many app one server" approach, where many different apps can be deployed to the same self-hosted Budibase platform. Below we have detailed a basic method for getting Budibase up and running behind a reverse proxy, which you can then update to add into an existing configuration or add SSL information.
+Use a server block that:
-Here is a basic reverse proxy configuration that will simply pass all requests from a domain/sub-domain/path to your Budibase platform.
+* Listens on the desired domain or subdomain
+* Proxies requests to the Budibase host
+* Passes through the forwarded headers Budibase needs
+* Sets an appropriate `client_max_body_size`
+Example:
-# Budibase
-```
+```nginx
server {
listen 80;
server_name yourdomain.com;
-
+
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
@@ -84,28 +74,26 @@ server {
proxy_http_version 1.1;
proxy_pass http://localhost:10000;
}
-
+
client_max_body_size 50m;
}
```
-In this configuration, all that needs to be updated for this to work is where the Budibase platform has been hosted. If it is hosted on a different machine, replace **localhost** with the correct address.
+## Common adjustments
-There are a few ways this can be extended/altered:
+If the proxy sits on another host, replace `localhost` with the correct upstream address.
-1. This is just a basic version which will proxy on any requests to your domain/sub-domain to the Budibase platform, you can change the **server_name** to suit your needs.
-2. Where **location /** has been set you can change the slash to a pathname if you desire to have your Budibase app server running on a specific path rather than the whole domain or a sub-domain.
-3. Lastly if you want to make use of HTTPS/TLS you can either configure this basic configuration to re-direct traffic to HTTPS and then add your certificate, or you can use [certbot-auto](https://certbot.eff.org/) to automatically generate and look after the certificates for your domain!
+If you want Budibase on a path rather than a whole domain, change the `location` block accordingly.
-## Proxy Buffer Settings
+If you enable TLS through NGINX, either add certificates manually or use a certificate tool such as Certbot.
-When Budibase is deployed behind an external reverse proxy such as Nginx Proxy Manager and external authentication is enabled, requests can fail with `502 Bad Gateway openresty`.
+## Proxy buffer issues
-This usually happens when response headers, such as a `Content-Security-Policy` header with many whitelisted domains, exceed the proxy buffer limit. Budibase reduces the likelihood of this by removing CSP headers from JSON API responses and increasing the internal proxy buffer size to 16k.
+If you see `502 Bad Gateway openresty` with external authentication, increase the proxy buffer sizes.
-If you still see this error when using an external proxy, increase the proxy buffer size. An example Nginx configuration is shown below:
+Example:
-```
+```nginx
server {
proxy_busy_buffers_size 512k;
proxy_buffers 4 512k;
@@ -114,32 +102,16 @@ server {
}
```
-## Strict Referrer Policy and other security settings
-
-There are a few caveats with reverse proxies that operators need to pay close attention to—especially when running Budibase behind Nginx, Traefik, Caddy, or any proxy layer that modifies or strips HTTP headers. Some security-focused defaults can unintentionally break core platform functionality.
+## Referrer policy
-### Referrer Policy and Workspace App Matching
+Budibase can rely on the `Referer` header when matching a workspace app to a request path.
-Budibase’s server **relies on the Referer header** in a specific part of the request lifecycle. The platform extracts the URL path from the incoming request’s Referer header. If a reverse proxy strips the Referer, changes it, or prevents it from flowing through, Budibase may fail to match the correct workspace app and respond with:
+If your proxy strips or rewrites that header, Budibase may fail to find the correct workspace app.
-```
-“No matching workspace app found for URL path…”
-```
-
-To preserve Budibase functionality while keeping sane security defaults, adjust your policy for the Budibase domain:
-
-```
-server {
- add_header Referrer-Policy same-origin;
+Keep the header available for the Budibase domain and use a conservative referrer policy that still preserves matching.
- location / {
- proxy_set_header Referer $http_referer;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header Host $host;
- proxy_pass http://your-budibase-upstream;
- }
-}
-```
+## Related guides
-
+* [Accessing CouchDB](doc:accessing-couchdb)
+* [Accessing MinIO](doc:accessing-minio)
+* [Hosting methods](doc:hosting-methods)
diff --git a/docs/Settings/_order.yaml b/docs/Settings/_order.yaml
index 87eb6c3b9..347b3c39e 100644
--- a/docs/Settings/_order.yaml
+++ b/docs/Settings/_order.yaml
@@ -12,6 +12,8 @@
- audit-logs
- backups-1
- metrics
+- email
+- email-templates
- branding
- v2-recaptcha
- translations
diff --git a/docs/Settings/authentication-and-sso/sso-with-keycloak.md b/docs/Settings/authentication-and-sso/sso-with-keycloak.md
index 4a444b9af..8f9990d54 100644
--- a/docs/Settings/authentication-and-sso/sso-with-keycloak.md
+++ b/docs/Settings/authentication-and-sso/sso-with-keycloak.md
@@ -12,78 +12,106 @@ next:
---
> 📘 OpenID Connect
>
-> To become familiar with OpenID Connect in Budibase, see: [OpenID Connect](doc:openid-connect)
+> To become familiar with OpenID Connect in Budibase, see [OpenID Connect](doc:openid-connect)
-## Configuration
+Use Keycloak to configure OIDC-based SSO in Budibase.
-
+## Realm setup
-### Realm Setup
+Use a realm other than `master`. In Keycloak, `master` is reserved for top-level administration.
-It's advised to use a realm other than the `master` realm in keycloak. The `master` realm in Keycloak contains your admin user and is meant to only be used as a top level admin space, where you can provision and manage other admin users and new realms.
+To create a new realm:
-To create a new realm, log in as your admin user at `/admin` and go to the realm dropdown on the left. Click "Create realm" and name your new realm, then save.
+1. Sign in to Keycloak at `/admin`.
+2. Open the realm dropdown on the left.
+3. Select `Create realm`.
+4. Save the new realm name.
-### Add a Client
+## Create a client
-The first step of configuring Keycloak for OIDC in Budibase is to create a client. Log into Keycloak admin console with your admin credentials:
+Create a client in Keycloak before you configure Budibase.
-Visit the 'Clients' pane on the left side of your Keycloak admin console and click the 'Create client' button.
+Open the **Clients** pane in the Keycloak admin console and click **Create client**.
-Give your new client an ID and select 'OpenID Connect' from the dropdown. Then click next. This client ID will be the one you use in Budibase to connect your Keycloak instance for OIDC authentication.
+Give the client an ID, select **OpenID Connect**, and continue. Use this client ID in Budibase.
-(If you have a client already created, bypass this step.)
+If you already have a suitable client, you can skip this step.
-### Capability config
+## Enable client authentication
-In the Capability config screen, toggle on 'Client authentication'. This will generate a **secret key** for the client, which you will use within the Budibase OIDC configuration later. Click the 'next' button.
+In the capability configuration screen, enable **Client authentication**.
+
+This generates the client secret you will use in Budibase.
-Next, we will get the Callback URL that we need to paste into Keycloak. You will find this in your Budibase portal under Settings > Auth > OpenID Connect.
+## Add the Budibase callback URL
+
+Copy the callback URL from Budibase under `Settings > Auth > OpenID Connect`.
-Copy the callback URL and add it to the 'Valid Redirect URIs' field in Keycloak then click 'Save'.
+In Budibase, this field is labeled **Callback URL**.
+
+Add that URL to **Valid Redirect URIs** in Keycloak, then save.
-### Find your configuration details
+## Find your configuration details
+
+### Config URL
+
+Open **Realm settings** in the Keycloak admin console, then open the **General** tab.
+
+Scroll to **OpenID Endpoint Configuration** and copy the URL.
+
+Paste the URL into the **Config URL** field in Budibase.
-#### Client ID
+### Client ID
-The Client ID field in Budibase shuld be the same ID as you gave the Client in Keycloak earlier.
+The Client ID in Budibase should match the client ID you created in Keycloak.
-#### Client Secret
+### Client secret
-You can find the Client Secret from the Credentials tab in the Client Configuration screen
+Find the Client secret on the **Credentials** tab in the client configuration screen.
-Add this to the "Client secret" field in your Budibase OIDC panel.
+Paste it into the **Client secret** field in the Budibase OIDC panel.
-#### Configuration URL
+### Optional settings
-Visit the 'Realms settings' pane on the left hand side of your Keycloak admin console. Click into the general tab at the top of this page. Scroll down to the bottom and then click on the **OpenID Endpoint Configuration** link and copy the URL.
+Budibase also lets you configure:
-You can then paste this URL into your Budibase OIDC Config as the Configuration URL, and Budibase will be integrated with Keycloak for OIDC.
+* **Name**: The text shown on the login button
+* **Icon**: The provider icon shown on the login button
+* **PKCE Method**: Available on enterprise licenses
+* **Activated**: Enables the OIDC provider
+* **Allow unverified email linking**: Only use this if you trust the identity provider to assert verified email addresses
+* **Auth Scopes**: Includes `openid` plus any additional scopes you need
-# Managing Users
+## Activate OIDC in Budibase
-Now that Budibase is set up to authenticate using OIDC through Keycloak, we need to make sure we have users set up in our realm. To create a new user, you need to go to the **Users** tab in the Keycloak admin panel under your realm, and click "Create user"
+After you finish the configuration fields, turn on **Activated** in the Budibase OpenID Connect settings and save.
-Make sure you enter a username and email address for your user.
+
-
+## Manage users
-To add a password for the user, click the "Credentials" tab of the new user, and add a password by clicking the "Set Password" button. If you don't want the user to have to change the password on first log in, make sure to toggle the "Temporary" toggle off.
+After Budibase is set up, create users in the Keycloak realm.
-Now the user is created, you can sign in as that user through Budibase OIDC using Keycloak!
+Open the **Users** tab in the Keycloak admin panel and click **Create user**.
-
+Enter a username and email address.
+
+
+
+To add a password, open the **Credentials** tab and click **Set Password**. Turn off **Temporary** if the user should not change the password on first sign in.
+
+You can now sign in through Budibase using Keycloak.
diff --git a/docs/Settings/branding.md b/docs/Settings/branding.md
index 1d87b1501..987c66366 100644
--- a/docs/Settings/branding.md
+++ b/docs/Settings/branding.md
@@ -10,141 +10,62 @@ metadata:
next:
description: ''
---
-There are several ways in which you can customise the branding of your Budibase instance and, of course, your applications!
+Use Branding to control the visual identity of your Budibase instance and applications.
-> 📘 Licensing🔒
+> 📘 Licensing
>
-> The following features are only available to customers with a **Premium** or **Enterprise** plan.
->
-> You can view more details on [our pricing page.](https://budibase.com/pricing/)
-
-In the Budibase **admin portal**, navigate to the to the branding section, **Settings > Branding**. In here you can manage all available elements of your Budibase branding configuration.
-
-{`
-
-`}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Logo
-
-Easily the most impactful part of your branding. Your custom logo will be visible in the **portal header**:and the **app navigation**
-
-You can, of course, update the logo on a per application basis. In the **builder design section**, you can override the global branding and configure a logo especially for your application navigation.
-
-For **self host** environments, your logo will also appear in your Budibase login page!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Favicon
-
-
-
-If a custom favicon has been set, it will be displayed in the tab for any **published** or **previewed** application.
-
-In **self host** environments, the favicon will also appear in your Budibase login and authentication pages!
-
-### Title (Self Host)
-
-For **self host** environments, the title attribute can be used to customise the tab text for your Budibase login and authentication pages.
-
-### Email Branding
-
-Emails sent from Budibase will carry our branding header, as shown below.If you would like to remove this from your Budibase emails and customise your correspondence, simply flip the toggle.
-
-
-
-### Login Customisation (Self Host)
-
-With a self hosted instance of Budibase you can further customise elements of the log in screen.
-
-
-
-#### Available options
-
-* **Header** - the main text below your branding. Currently this defaults to **Log in to Budibase**
-* **Button** - the text displayed on the login button. Currently this defaults to **Log in to Budibase**.
-* **Remove Testimonials** - Testimonials currently default to **enabled** but you can flip the toggle and remove them if you wish
-
-Your fully customised home page:
+> Branding options are available on Premium and Enterprise plans.
-
+## Branding settings
-### App Preview Meta tags
+Open `Settings > Branding` in the admin portal to manage global branding.
-Meta tags are primarily used to describe your application to search engines or when sharing a link to your application via social media.
+Common settings include:
-If you post the link to your application on e.g **Facebook**, **Twitter** or **Slack**, you would typically see a card that looks something like this: In the branding section, you can customise the look and feel of your applications meta data with the following **optional** properties.
+* Logo
+* Favicon
+* Title for self-hosted login pages
+* Email branding
+* Login screen text and buttons for self-hosted instances
+* App preview meta tags
-
+## Logo
-
+Use the logo setting to control the branding shown in the portal, app navigation, and self-hosted login pages.
-
+If a specific app needs different branding, override the global logo in the app builder.
-
+## Favicon
-
+Set a custom favicon to change the browser tab icon for published and previewed apps.
-
+On self-hosted instances, the favicon can also appear on login and authentication pages.
-
+## Email branding
-#### Image URL
+Use email branding to remove or replace the Budibase header in outgoing emails.
-Use the **Image URL** property to configure a custom branding image URL.
+Keep the sender identity and the email template aligned so the message feels consistent.
-Ensure the target image is **appropriately sized** for the media preview of your target platforms.
+## Self-hosted login customisation
-Lastly, be mindful of the **availability** of the file when configuring this property. If the file is inaccessible when sharing your application link, the card will not render as intended.
+Use these fields to customise the login page header and button text.
-#### Title
+These values apply to the Budibase login page and can be used to match your organisation’s terminology.
-The default behaviour for the meta title combines the **application name** with **"- built with budibase."**.
-For example, if your application was called "My Expenses Portal", it would be displayed as **"My Expenses Portal - built with budibase."**
+## App preview meta tags
-You can use the **Title** property to override this behaviour. Any value set here will **override all app titles** displayed in meta cards.
+Use meta tags to control how your app appears when shared in chat or social previews.
-#### Description
+Configure:
-The description field is entirely optional. A description typically provides supplementary information about the target, or in this case, additional information about your organisation/apps.
+* Image URL
+* Title
+* Description
-#### App Meta Card example
+Make sure the preview image is publicly reachable when the link is shared.
-The following is an example of some of the custom meta card elements in action. Our custom branding has replaced the Budibase default and we now have our own description.
+## Related guides
-
+* [Email](doc:email)
+* [User management](doc:user-management)
diff --git a/docs/Settings/email-templates.md b/docs/Settings/email-templates.md
new file mode 100644
index 000000000..f3d774079
--- /dev/null
+++ b/docs/Settings/email-templates.md
@@ -0,0 +1,60 @@
+---
+title: Email templates
+excerpt: Manage Budibase's built-in email templates.
+deprecated: false
+hidden: false
+metadata:
+ title: ''
+ description: ''
+ robots: index
+next:
+ description: ''
+---
+Budibase includes ready-made email templates for common system messages.
+
+Open `Settings` > `Email` > `Templates` to review, edit, preview, and export them.
+
+## Available templates
+
+The built-in templates cover common email flows such as:
+
+* Password recovery
+* Invitations
+* Welcome emails
+
+If custom templates are enabled in your workspace, they also appear in this list.
+
+## Open a template
+
+1. Open `Settings` > `Email` > `Templates`.
+2. Select a template from the table.
+3. Edit the contents.
+4. Preview the result.
+5. Save your changes.
+
+## Edit template content
+
+The template editor supports Handlebars bindings.
+
+Use bindings for values such as:
+
+* Recipient email address
+* Organisation name
+* Logo URL
+
+On the edit screen, the bindings panel is split into:
+
+* Template bindings
+* Common bindings
+
+## Preview templates
+
+Use the Preview tab to see the rendered email content before saving.
+
+## Export templates
+
+The templates list page includes export actions for the default template set.
+
+If custom templates exist, Budibase also shows an export action for them.
+
+This is useful when you want to back up template changes or move them between environments.
diff --git a/docs/Settings/email.md b/docs/Settings/email.md
index 6aabc40cd..bbb2d0cbc 100644
--- a/docs/Settings/email.md
+++ b/docs/Settings/email.md
@@ -9,151 +9,73 @@ metadata:
robots: index
next:
description: ''
+ pages:
+ - type: basic
+ slug: email-templates
+ title: Email Templates
---
-For Budibase to send emails, you must configure an SMTP Mail Server, such as Gmail SMTP or SendGrid. After you have set this up, you can [invite users](doc:user-management) and send emails using the email [Action](doc:automation-actions).
-
-### Email setup
-
-{`
-
-`}
-
-
-
-
-
- Property
-
-
-
- Description
-
-
-
- Example answer
-
-
-
-
-
-
-
- Host
-
-
-
- An SMTP email server will have an address (or addresses) that can be set and is generally formatted as smtp.serveraddress.com.
-
-
-
- email-smtp.eu-east-1.amazonaws.com
-
-
-
-
-
- Security type
-
+Configure SMTP so Budibase can send emails for invitations, password recovery, and automation actions.
-
- Both SSL and TLS facilitate email sending
-
+## Before you start
-
- None/STARTTLS
-
-
+Make sure you have:
-
-
- Port
-
+* An SMTP provider such as Gmail SMTP or SendGrid
+* The host, port, username, and password for that provider
+* Access to the Budibase admin portal
-
- An “SMTP port” refers to the specific part of the Internet address that’s used to transfer email.
+## Configure SMTP
- If you run your own SMTP server you can find the configured SMTP port number and address from the SMTP server configuration.
+1. Open the Budibase admin portal.
+2. Go to `Settings > Email`.
+3. Enter the SMTP details.
+4. Save the configuration.
- Ports 25, 465, 587, or 2525 for SMTP have all been considered standard SMTP ports at some point, but only 587 or 2525 really should be considered for modern use.
-
+### SMTP settings
-
- 587
-
-
+| Setting | Purpose |
+| :--- | :--- |
+| Host | SMTP server address. |
+| Security type | Encryption mode used by the server. |
+| Port | SMTP port exposed by the server. |
+| From email address | Address used as the sender. |
+| Require sign-in | Enables SMTP authentication. |
+| Username | SMTP account username. |
+| Password | SMTP account password. |
-
-
- Default from email address
-
+Use the values required by your provider. For modern SMTP setups, ports `587` and `2525` are the most common choices.
-
- Used by system emails and SMTP automations that do not specify a Send From address. Your SMTP provider may restrict or rewrite this address.
-
+Budibase email templates are managed on a separate page.
-
-
- Require sign-in
-
+See [Email templates](doc:email-templates) for the available templates and how to edit them.
-
- SMTP auth - some SMTP server hosts require auth to send email.
-
+## Use email in automations
-
- True udibase to se
-
-
+Once SMTP is configured, you can send email from automation actions.
-
-
- Username (visible when require sign-in is checked)
-
-
-
- Username for SMTP server
-
-
-
- AKIAX5ZKVGGJ2SASAHT2M7Z
-
-
+Common uses include:
-
-
- Password (visible when require sign-in is checked)
-
+* User invitations
+* Password recovery
+* Workflow notifications
+* Approval and rejection messages
-
- Password for SMTP server
-
+Keep sender addresses and template content aligned with your domain so mail is less likely to be flagged as suspicious.
-
- Password!23IhopeNot
-
-
-
-
+## Troubleshooting
-
+If email does not send:
-## Templates
+* Confirm the SMTP host and port are correct
+* Check whether authentication is required
+* Verify the from address is allowed by the provider
+* Confirm the provider is not blocking the connection
-Budibase comes with three different email templates: Password Recovery, Invitation, and Welcome. In addition to that, you can also create custom templates.
+## Related guides
-To edit a template, simply select it in the table. To insert dynamic content you can use the Bindings on the right side of the page. This makes it easier to include things such as the user's email, your organization's logo, or the name of your organization. When you're done hit the preview button to make sure it looks OK. Then hit save.
-
-The email templates work on all devices, email platforms, and themes (light and dark modes). Below is an image of the invitation email template.
-
-
-
-## Video tutorial
-
-{`
-
-`}
\ No newline at end of file
+* [Automation actions](doc:automation-actions)
+* [User management](doc:user-management)
+* [Branding](doc:branding)
+* [Email templates](doc:email-templates)
diff --git a/docs/Settings/user-management/index.md b/docs/Settings/user-management/index.md
index 65533227c..115bfa4c0 100644
--- a/docs/Settings/user-management/index.md
+++ b/docs/Settings/user-management/index.md
@@ -1,8 +1,7 @@
---
title: User management
excerpt: >-
- The user management page contains all you'll need to invite, delete and create
- new users as well as change their Budibase application roles.
+ Manage users, roles, groups, and SCIM provisioning across Budibase.
deprecated: false
hidden: false
metadata:
@@ -18,21 +17,59 @@ next:
- type: basic
slug: user-roles
title: User Roles
+ - type: basic
+ slug: workspace-access
+ title: Workspace Access
+ - type: basic
+ slug: in-app-user-management
+ title: In-app User Management
+ - type: basic
+ slug: user-groups
+ title: User Groups
+ - type: basic
+ slug: scim-provisioning
+ title: SCIM Provisioning
---
-In Budibase, users are shared across your whole Budibase installation. Users can sign into any app that they have access to. To give users access to specific apps, check the [User Roles](doc:user-roles) page.
+Use user management to control who can sign in, what access they have, and how they are grouped across Budibase.
+
+Users are shared across the whole Budibase installation.
+
+## Invite users
+
+You can invite users from two places:
+
+* The `Invite users` button in the builder sidebar
+* `Settings` > `People` in the Budibase portal
+
+Use [Adding users](doc:adding-users) for the full invite flow.
+
+## Reset passwords
+
+If a user needs a password reset:
+
+1. Open the user list.
+2. Open the user menu next to the email address.
+3. Select `Force password reset`.
+
+Budibase generates a temporary password and prompts the user to change it after sign-in.
+
+## Roles
-> 📘 Adding new users
->
-> Invite users from the workspace **Invite user** flow. Read more in [Adding Users](doc:adding-users).
+Users can have both tenant-level and workspace-level access.
-## Resetting passwords
+Use `User Roles` to configure access to specific apps and workspaces.
-If you need to reset a users' password, you need to click the ellipsis (...) next to their email, and `Force password reset` button on the users` detail page. You get displayed a new password for the user with which they can only log in once. Once logged in they will be prompted to change their password before continuing on.
+## Delete users
-## Configure user roles
+Delete users from the user menu when they no longer need access.
-Users can have roles for both the Budibase installation and workspaces, read more about that in [User Roles](doc:user-roles).
+Treat deletion as permanent. If you need the user again later, invite them again instead.
-## Deleting a user
+## Related guides
-In the ellipsis menu next to the user's email address, you can find a Delete user option. Click the `delete user` button and confirm in the dialogue. Keep in mind deleting users _cannot_ be undone.
+* [Adding Users](doc:adding-users)
+* [User Roles](doc:user-roles)
+* [Workspace Access](doc:workspace-access)
+* [In-app User Management](doc:in-app-user-management)
+* [User Groups](doc:user-groups)
+* [SCIM Provisioning](doc:scim-provisioning)
diff --git a/docs/Settings/user-management/scim-provisioning.md b/docs/Settings/user-management/scim-provisioning.md
index e58038b8d..8dd4fcee6 100644
--- a/docs/Settings/user-management/scim-provisioning.md
+++ b/docs/Settings/user-management/scim-provisioning.md
@@ -10,146 +10,108 @@ metadata:
next:
description: ''
---
-SCIM (System for Cross-domain Identity Management) is an open standard for managing user identity information.
+SCIM lets you manage users and groups in an external identity provider and sync them into Budibase.
-It allows you to manage your users and groups outside of Budibase using any Identity and Access Management service that supports SCIM. Examples include *Azure Active Directory* and *Okta*.
+It is designed for one-way provisioning:
-Changes to users and groups made in your external IAM can be synced across to Budibase users and groups through a process known as **provisioning**.
+* Budibase receives users and groups from your identity provider.
+* Synced users and groups are then managed externally.
+* Manual users and groups can still be created and edited in Budibase.
-It's worth noting that this is a one-way process. Once SCIM is enabled, you will no longer be able to edit AD users and groups within Budibase, any changes must be made via your external identity provider.
+Common providers include Azure Active Directory and Okta.
-You can still manually add or import users outside your organization; these users will remain fully configurable within Budibase.
+## Access
-## Unlocking SCIM provisioning
+SCIM provisioning is available on the enterprise plan.
-Contact sales to enquire about upgrading to the enterprise plan [https://budibase.com/contact/](https://budibase.com/contact/)
+Contact sales to upgrade: [budibase.com/contact](https://budibase.com/contact/)
## Enable SCIM
-Login to the Budibase portal, and click on the `Settings` tab. Select the `Auth` tab.
+Open `Settings` > `Auth` in the Budibase portal.
-
+Turn on `Activated` under the SCIM section.
-Scroll to the bottom of the page, and under SCIM toggle *Activated* on. The provisioning *URL* and *Token* will become available to quickly copy using the clipboard buttons to the right of the fields.
+When SCIM is enabled, Budibase shows:
-
+* `Provisioning URL`
+* `Provisioning Token`
-> 👍 Enforced SSO
->
-> When using SCIM, users can only log-in to Budibase using [SSO](doc:authentication-and-sso). With this in mind, we recommend that you [Enforce SSO 🔒](doc:enforced-sso).
+Copy both values into your identity provider.
-***
+The provisioning URL uses the current platform URL and the SCIM endpoint:
-## Example: Azure Active Directory
+```text
+{platformUrl}/api/global/scim/v2
+```
-This example will look at provisioning active directory users from Azure into Budibase
+The provisioning token is the current user API key shown in the SCIM settings screen.
-### Step 1 - Create an enterprise application
+## SSO requirement
-First, we need to create an Enterprise application to manage the users and groups that we want to provision for Budibase.
+SCIM provisioning does not disable password-based sign-in on its own.
-Log in to Azure Active Directory, and Navigate to `Enterprise applications`.
+If you want to require SSO-only login, enable [Enforce SSO](doc:enforced-sso) separately.
-Click on `New application`
+## Disable SCIM
-
+If SCIM is already enabled and you turn it off, Budibase asks what to do with existing SCIM-provisioned users.
-Then click on `Create your own application`. A side panel should appear on the right hand side of your screen.
+You can choose between:
-
- Create new application
-
+* Remove SCIM users
+* Convert to regular users
-Select the *(Non-gallery)* option and give your app a name.
+Removing SCIM users deletes them permanently.
-
+Converting them keeps the users in Budibase, but they are no longer synced with your identity provider.
-Assuming you already have some users in your active directory, you can now add them to your enterprise application.
+## Manage synced users
-Click on your application, and then click `Users and groups` under the *Manage* section. You can then click on `Add user/group` to add users individually or as user groups.
+Synced users are matched by email.
-
+When a user is managed through SCIM:
-### Step 2 - Provisioning
+* Their email, first name, and last name are read-only in Budibase.
+* Other non-synced fields can still be edited.
+* The user is marked as externally synced in the builder.
-Under the *Manage* section, click on `Provisioning`. Then click `Connect your application`.
+## Manage synced groups
-
+Synced groups are matched by name.
-Copy and paste the URL and Token from the Budibase settings page as the admin credentials.
+When a group is managed through SCIM:
-
+* The group name is read-only in Budibase.
+* Non-synced fields can still be edited.
+* The group is marked as externally synced in the builder.
-Click `Test Connection` to verify everything is correct. If all the information is correct, a notification will appear saying the connection was successful. The Create button at the bottom of this page will change from grayed out to blue when you successfully test the Budibase connection. Click the `Create` button.
+## First sync
-You will then be redirected to the Overview area for Provisioning. You can now click `Start provisioning` to sync your users and groups from your Azure enterprise app into Budibase.
+When SCIM is activated for the first time:
-
+* Existing Budibase users are matched to SCIM users by email.
+* Existing groups are matched by name.
+* Members already assigned to matched groups are replaced by the SCIM-provisioned members.
-The Budibase portal now shows the users who have synced under the `Users` tab.
+## Azure Active Directory example
-
+If you are using Azure AD, the provisioning flow is:
-There is also a note in this section indicating that users are being synced from your AD.
+1. Create an enterprise application in Azure AD.
+
+2. Add the users and groups you want to provision.
+3. Open Azure provisioning settings and connect the application.
+
+ Create new application
+
+4. Paste the Budibase provisioning URL and token into Azure.
+
+
+5. Test the connection, create the provisioning setup, and start provisioning.
+
+6. Give the provisioned users app access in Budibase.
-If you provide a first and last name for your Azure users, this will also be passed through, however other settings such as [User roles](doc:user-roles) and [App roles](https://docs.budibase.com/docs/user-roles#app-specific-roles) must be set within Budibase.
+If you also need SSO, follow the [SSO with Azure AD](doc:sso-with-azure-ad) guide.
-
-
-
-
-### Step 3 - Setup SSO and give app access
-
-Finally, we need to ensure that provisioned users can log in to Budibase. In this case, you can follow the [SSO with Azure AD](doc:sso-with-azure-ad) guide.
-
-Furthermore, make sure you have given your users and groups [Application access](doc:application-access).
-
-
- Giving Carol basic app access
-
-
-{`
-
-`}
-
-## Managing SCIM
-
-### Initial SCIM Sync Activation
-
-#### What happens when a SCIM sync is activated for the first time?
-
-* User Matching
- * Existing Budibase users are matched with SCIM users by email.
- * Matched users are marked as externally synced and managed externally thereafter.
-* Group Matching:
- * Existing groups are matched by name.
- * Groups with matching names (e.g., “engineering”) are taken over as externally synced.
-* User Assignment in Synced Groups:
- * Existing assigned users will be wiped out
- * SCIM specified users will be mapped to the group.
-
-### Managing SCIM Users
-
-#### How to manage SCIM users?
-
-* Identifying Synced Users:
- * Synced users are marked with a sync logo in the Budibase builder.
- * Certain fields will be read-only for synced users.
-* Fields for Synced Users:
- * Read-Only Fields: Email, name, and surname (managed via external SCIM datasource).
- * Editable Fields: Role and other non-synced fields can be edited in Budibase.
-* Non-SCIM Users:
- * Users created manually or not matched with SCIM are fully editable in Budibase.
-
-### Managing SCIM Groups
-
-### How to manage SCIM groups?
-
-* Identifying Synced Groups:
- * Synced groups are marked with a sync logo in the Budibase builder.
- * Some fields will be read-only for synced groups.
-* Editable Fields for Synced Groups:
- * Read-Only Fields: Group name (managed via SCIM).
- * Editable Fields: Icons and other non-synced fields can be edited in Budibase.
-* Non-SCIM Groups:
- * Groups created manually or not matched with SCIM are fully managed in Budibase.
\ No newline at end of file
+After provisioning, assign the required [user roles](doc:user-roles) and [application access](doc:application-access) in Budibase.
diff --git a/docs/Settings/user-settings.md b/docs/Settings/user-settings.md
index 5e3f5c5ed..9d227641e 100644
--- a/docs/Settings/user-settings.md
+++ b/docs/Settings/user-settings.md
@@ -10,29 +10,36 @@ metadata:
next:
description: ''
---
-Budibase provides some options for configuring your user account. Access the user settings menu by clicking on your profile dropdown in the top right of the builder.
+Use the User settings page to manage your profile, theme, layout, and API key.
-
+Open it from the user menu in the builder sidebar.
-Let's go through the options one by one.
+## My Profile
-### Update user information
+Use this section to update your email, first name, and last name.
-Update your first and last name for your user account. This is useful for things like email templates within Budibase, and will allow other people in your organisation to identify you.
+Keeping these details current helps with email templates and makes it easier for other people in your organisation to identify you.
-
+## Theme
-### Update password
+Use this section to update the Budibase portal theme.
-Here, you can update the password for your own user account.
+## API key
-
+This section displays your API key and a copy button.
-### Toggle developer mode (Developer/Admin users only)
+Use this value as the required header for the [Public API](doc:public-api).
-As a developer/admin, you can switch between viewing the portal in **developer mode** and **standard mode**. Find out more about the different budibase [Portal](doc:portal) modes.
+## Update password
-### View API key
+Use this action to open the password change flow.
-Displays the API key with a handy copy button.
-This is a required header when using the [Public API](doc:public-api)
\ No newline at end of file
+If your account is not using SSO, Budibase lets you update the password directly from the user menu or the profile modal.
+
+If you are the account portal owner in a cloud workspace, Budibase opens the account portal account page instead.
+
+## Close developer mode
+
+Use this action to return from the builder back to the apps view.
+
+This closes developer mode and takes you to the main workspace app list.