Merge branch 'develop'

This commit is contained in:
Akshay Sasidharan 2022-09-01 22:47:46 +05:30
commit fd15d12db9
140 changed files with 25409 additions and 8713 deletions

View file

@ -0,0 +1,22 @@
---
name: 🛠 Refactor Codebase
about: Refactoring code
labels: 'refactor'
title: "[refactor]: "
---
### What area of the codebase is to be refactored?
<!--
Provide a description of the codebase area to be refactored.
-->
(Write your answer here.)
### What are the potential advantages of this proposed refactoring?
<!--
Provide a description of the potential advantages of the proposed refactoring.
-->
(Describe the advantages here.)

View file

@ -82,6 +82,7 @@ Want to give ToolJet a quick spin on your local machine? You can run the followi
```bash
docker run \
--name tooljet \
--user root \
--restart unless-stopped \
-p 3000:3000 \
-v tooljet_data:/var/lib/postgresql/13/main \

View file

@ -15,15 +15,15 @@ ENV NODE_OPTIONS="--max-old-space-size=4096"
COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/
RUN npm --prefix plugins install
COPY ./plugins/ ./plugins/
ENV NODE_ENV=production
RUN npm --prefix plugins run build
RUN NODE_ENV=production npm --prefix plugins run build
RUN npm --prefix plugins prune --production
# Build frontend
COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/
RUN npm --prefix frontend install --only=production
RUN npm --prefix frontend install
COPY ./frontend ./frontend
RUN npm --prefix frontend run build
RUN npm --prefix frontend run build --production
RUN npm --prefix frontend prune --production
FROM openresty/openresty:1.19.9.1rc1-buster-fat

View file

@ -15,15 +15,17 @@ COPY ./package.json ./package.json
COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/
RUN npm --prefix plugins install
COPY ./plugins/ ./plugins/
ENV NODE_ENV=production
RUN npm --prefix plugins run build
RUN NODE_ENV=production npm --prefix plugins run build
RUN npm --prefix plugins prune --production
# Build frontend
COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/
RUN npm --prefix frontend install
COPY ./frontend/ ./frontend/
RUN npm --prefix frontend run build
RUN npm --prefix frontend run build --production
RUN npm --prefix frontend prune --production
ENV NODE_ENV=production
# Build server
COPY ./server/package.json ./server/package-lock.json ./server/

View file

@ -0,0 +1,29 @@
---
id: white-label
title: White Label
---
# White Label
White Label feature will allow you to remove the ToolJet branding from the ToolJet platform and add your own custom logo and text.
This feature allows you to rebrand the following:
- **App logo** (Logo on login screen, dashboard, and app-editor)
<img className="screenshot-full" src="/img/enterprise/white-label/applogo.png" alt="ToolJet - Enterprise - White label" height="420"/>
<img className="screenshot-full" src="/img/enterprise/white-label/appeditor.png" alt="ToolJet - Enterprise - White label" height="420"/>
- **Favicon**
- **Page Title** (next to Favicon)
<img className="screenshot-full" src="/img/enterprise/white-label/favicon.png" alt="ToolJet - Enterprise - White label" height="420"/>
## Configuration
To enable white labelling, you'll need to set the below mentioned **environment variables** in the .env file:
- `WHITE_LABEL_LOGO`: URL of the logo. Preferred dimensions of the logo are: width 130px and height 26px
- `WHITE_LABEL_TEXT`: The text that you want to display as Page Title
- `WHITE_LABEL_FAVICON`: URL of the favicon. Preferred dimensions of the logo are: 16x16px or 32x32px

View file

@ -0,0 +1,17 @@
---
id: close-modal
title: Close modal
---
Use this action to close the modal that is already shown.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Open webpage](/img/actions/closemodal/closemodal.png)
</div>

View file

@ -0,0 +1,97 @@
---
id: control-component
title: Control component
---
Control component action invokes the component specific actions. Component specific actions are the actions that are exclusive actions for a particular widget. Component specific actions can be triggered either through the event handlers or from the Run JavaScript code query.
:::info
Check out the **[live demo](https://youtu.be/JIhSH3YeM3E)** of Component specific actions demonstrated in one of our community call.
:::
## Available Component Specific Actions
| Widget | Component Specific Actions |
|--------|---------------------------|
| Button | Click, Set label |
| Text | Set text |
| Text Input | Set text, Clear |
| Text Area | Set text, Clear |
| Modal | Show, Close |
| Table | Set page |
| Dropdown | Select option |
| Multiselect | Select option, Deselect option, Clear selection |
| Map | Set location |
| Checkbox | Set checked |
| Radio button | Select option |
| Tabs | Set tab |
| Color picker | Set color |
| File picker | Clear files |
:::info
Currently, Component specific actions are supported only by the above listed widgets. We are working on bringing component specific actions for the remaining widgets.
:::
## Using Component Specific Actions
### Set a value for text input widget using button's event handler
- Drag a **Text Input** and a **Button** widget onto the canvas.
- Go to the **Inspector** on the left sidebar to check the exposed variables available for the `textinput1` widget under the `components`. You'll see that the variable `value` is an empty string because the field value of the text input widget is empty right now.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/inspector.png)
</div>
- Now enter some value in the text input widget and you'll see that the `value` in inspector has been updated.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/updated.png)
</div>
- Now, click on the button's widget handler to open up its properties in the right sidebar and then add a event handler for **On Click** event to trigger **Control Component** action. Select `textinput1` in component dropdown, `Set text` as Action, and in `Text` field enter the text that you want to update in the field value.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/button.png)
</div>
- Now when you'll click on the button you'll see that the field value of the text input widget has been updated with value that you set.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/set.png)
</div>
### Clear value of text input widget using JavaScript query
- Let's clear the value that we set in the previous section, using Run JavaScript code. Create a new Run JavaScript Code query and call the component and the CSA that component provides.
**Syntax:**
```js
await components.textinput1.clear()
```
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/jsoption.png)
</div>
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/clear.png)
</div>
- Finally, hit the **save and run** query button to fire up the query, and you'll see that the field value of the text input widget has been cleared.

View file

@ -0,0 +1,16 @@
---
id: copy-to-clipboard
title: Copy to clipboard
---
Use this action to copy the text to the clipboard.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Open webpage](/img/actions/copytoclipboard/copytoclipboard.png)
</div>

View file

@ -0,0 +1,18 @@
---
id: go-to-app
title: Go to app
---
This action allows you to open any ToolJet application when an event occurs.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Open webpage](/img/actions/gotoapp/gotoapp.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: logout
title: Logout
---
This action allows you to log out of the application (ToolJet).
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Logout](/img/actions/logout/logout.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: open-webpage
title: Open webpage
---
You can use this action to open a webpage(on a new tab) for any event.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Open webpage](/img/actions/open-webpage/open.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: run-query
title: Run Query
---
This action allows you to fire queries when an event occurs.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Run Query](/img/actions/run-query/run-query.png)
</div>

View file

@ -10,19 +10,46 @@ This action allows you to specify a `key` and its corresponding `value` to be st
## Example: App that stores a name in localStorage and displays it on reload
1. Add an input field, button and a text as shown
<img src="/img/actions/localstorage/sample-app-1.png" alt="Set local storage sample app" height="350" />
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/1.png)
</div>
2. Select the button and add a `Set localStorage` action with `key` set to `name` and value pointing at the value of the text field
<img src="/img/actions/localstorage/sample-app-2.png" alt="Set local storage sample app" height="350" />
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/2.png)
</div>
3. Select the text label we've added and set its value to the name item from localStorage
<img src="/img/actions/localstorage/sample-app-3.png" alt="Set local storage sample app" height="150" />
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/3.png)
</div>
4. Now save the application, this is important as we're about to reload the page.
5. Type in anything you wish on the input box and click on the button
<img src="/img/actions/localstorage/sample-app-4.png" alt="Set local storage sample app" height="150" />
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/5.png)
</div>
6. Reload the page, you'll see that the value stored in local storage is persisted and it is displayed on screen!
<img src="/img/actions/localstorage/sample-app-5.png" alt="Set local storage sample app" height="350" />
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/6.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: set-table-page
title: Set Table Page
---
Use this action to change the page index in the table widget.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Open webpage](/img/actions/settablepage/settablepage.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: set-variable
title: Set variable
---
This action allows you to create a variable and assign a `value` to it.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set variable](/img/actions/setvar/setvar.png)
</div>

View file

@ -0,0 +1,21 @@
---
id: show-alert
title: Show alert
---
This action allows you to display an alert message.
You can set a custom **message** for the alert and choose a particular alert type.
There are 4 types of alert messages - **Info**, **Success**, **Warning**, and **Danger**.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Show Alert](/img/actions/show-alert/show-alert.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: show-modal
title: Show modal
---
Use this action to show the modal for an event.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Open webpage](/img/actions/showmodal/showmodal.png)
</div>

View file

@ -0,0 +1,16 @@
---
id: unset-variable
title: Unset variable
---
This action allows you to removev the variable variable that was created using the set variable action.
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set variable](/img/actions/unsetvar/unsetvar.png)
</div>

View file

@ -0,0 +1,56 @@
---
id: slack
title: Slack
---
# Slack
ToolJet can connect to your Slack workspace to send messages.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Slack](/img/datasource-reference/slack/connect.png)
</div>
## Connection
- To add the Slack datasource, click the **Datasource manager** icon on the left-sidebar of the app builder and click on the `Add datasource` button, then select Slack from the modal that pops up.
- In the next dialog, you'll be asked to choose the **permission scope**. Choose the permission scope and then click on **Connect to Slack** button.
- A new tab will open up asking for aurization confirmation. Once done, you can close the tab.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Slack](/img/datasource-reference/slack/authorize.png)
</div>
- Click on the '**Save data source** button to save the data source.
## Supported operations
1. **List members**
2. **Send message**
### List members
This operation will return the data of all the members in your slack workspace.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Slack](/img/datasource-reference/slack/listmembers.png)
</div>
### Send message
This operation will send the message to specified channel in your slack workspace.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Slack](/img/datasource-reference/slack/sendmessage.png)
</div>

View file

@ -0,0 +1,192 @@
---
id: stripe
title: Stripe
---
# Stripe
ToolJet can connect to your Stripe account to read or write customers' and payments' data.
:::info
Check out the **[Stripe Refund App tutorial](https://blog.tooljet.com/build-a-stripe-refund-tool-using-low-code/)**
:::
## Connection
To add a new Stripe data source, click on the `+` button on data sources panel at the left-bottom corner of the app editor. Select Stripe from the modal that pops up.
ToolJet requires the **Stripe API key** to connect to your database.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Stripe](/img/datasource-reference/stripe/connect.png)
</div>
You can get the Stripe API key from the dashboard of your Stripe account. Go to the Stripe account dashboard, click on the **Developers** on the top right, then on the left-sidebar go to the **API Keys**, you can simple reveal the **Secret Key** and copy-paste on ToolJet.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Stripe](/img/datasource-reference/stripe/apikey.png)
</div>
## Querying Stripe
Click on **+** button of the query manager at the bottom panel of the editor and select the Stripe datasource added in the previous step. Enter the query in the editor. Click on the `Save and Run` button to save and then run the query.
**NOTE**: Query should be saved before running.
:::tip
Query results can be transformed using transformations. Read our transformations documentation to see how: **[link](/docs/tutorial/transformations)**
:::
## Supported operations
You can check out the some of the operations mentioned below. All the operations for Stripe are available and can be performed from ToolJet. Check out the **[Stripe API documentation](https://stripe.com/docs/api/)** for the detailed information about each operation.
- **delete,/v1/account**
- **get,/v1/account**
- **post,/v1/account**
- **post,/v1/account/bank_accounts**
- **delete,/v1/account/bank_accounts/{id}**
- **get,/v1/account/bank_accounts/{id}**
- **post,/v1/account/bank_accounts/{id}**
- **get,/v1/account/capabilities**
- **get,/v1/account/capabilities/{capability}**
- **post,/v1/account/capabilities/{capability}**
- **get,/v1/account/external_accounts**
- **post,/v1/account/external_accounts**
- **delete,/v1/account/external_accounts/{id}**
- **get,/v1/account/external_accounts/{id}**
- **post,/v1/account/external_accounts/{id}**
- **post,/v1/account/login_links**
- **get,/v1/account/people**
- **post,/v1/account/people**
- **delete,/v1/account/people/{person}**
- **get,/v1/account/people/{person}**
- **post,/v1/account/persons**
- **delete,/v1/account/persons/{person}**
- **get,/v1/account/persons/{person}**
- **post,/v1/account/persons/{person}**
- **post,/v1/account_links**
- **get,/v1/accounts**
- **post,/v1/accounts**
- **delete,/v1/accounts/{account}**
- **get,/v1/accounts/{account}**
- **post,/v1/accounts/{account}**
- **post,/v1/accounts/{account}/bank_accounts**
- **delete,/v1/accounts/{account}/bank_accounts/{id}**
- **get,/v1/accounts/{account}/bank_accounts/{id}**
- **get,/v1/accounts/{account}/bank_accounts/{id}**
- **get,/v1/accounts/{account}/capabilities**
- **get,/v1/accounts/{account}/capabilities/{capability}**
- **post,/v1/accounts/{account}/capabilities/{capability}**
- **get,/v1/accounts/{account}/external_accounts**
- **post,/v1/accounts/{account}/external_accounts**
- **delete,/v1/accounts/{account}/external_accounts/{id}**
- **get,/v1/accounts/{account}/external_accounts/{id}**
- **get,/v1/accounts/{account}/external_accounts/{id}**
- **post,/v1/accounts/{account}/login_links**
- **get,/v1/accounts/{account}/people**
- **post,/v1/accounts/{account}/people**
- **delete,/v1/accounts/{account}/people/{person}**
- **get,/v1/accounts/{account}/people/{person}**
- **post,/v1/accounts/{account}/people/{person}**
- **get,/v1/accounts/{account}/persons**
- **post,/v1/accounts/{account}/persons**
- **delete,/v1/accounts/{account}/persons/{person}**
- **get,/v1/accounts/{account}/persons/{person}**
- **post,/v1/accounts/{account}/persons/{person}**
- **post,/v1/accounts/{account}/reject**
- **get,/v1/apple_pay/domains**
- **post,/v1/apple_pay/domains**
- **delete,/v1/apple_pay/domains/{domain}**
- **get,/v1/apple_pay/domains/{domain}**
- **get,/v1/application_fees**
- **get,/v1/application_fees/{fee}/refunds/{id}**
- **post,/v1/application_fees/{fee}/refunds/{id}**
- **get,/v1/application_fees/{id}**
- **post,/v1/application_fees/{id}/refund**
- **get,/v1/application_fees/{id}/refunds**
- **post,/v1/application_fees/{id}/refunds**
- **get,/v1/apps/secrets**
<!--
### delete,/v1/account
This operation can be used to delete the accounts that you manage in Stripe.
#### Required parameters:
- **account**: Enter the account id of account that you want to delete. example: `acct_1032D82eZvKYlo2C`
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Firestore](/img/datasource-reference/stripe/delete-account.png)
</div>
### get,/v1/account
This operation returns the basic account information such as account id, capabilities, currency, country etc.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Firestore](/img/datasource-reference/stripe/get-account.png)
</div>
### post,/v1/account
This operation updates the connected account by setting the values of the parameters passed. Any parameters not provided are left unchanged.
### post,/v1/account/bank_accounts
This operation will create a bank account in your stripe account.
### delete,/v1/account/bank_accounts/{id}
This operation can be used to delete a specified external account for a given account.. You'll need to provide the **id** of the bank account in stripe.
### get,/v1/account/bank_accounts/{id}
This operation can be used to retrieve a specified external account whose **id** is provided in parameters.
### post,/v1/account/bank_accounts/{id}
This operation can be used to update the metadata, account holder name, account holder type of a bank account belonging to a Custom Account, and optionally sets it as the default for its currency. Other bank account details are not editable by design. You can re-enable a disabled bank account by performing an update call without providing any arguments or changes.
### get,/v1/account/capabilities
This operation returns a list of capabilities associated with the account. The capabilities are returned sorted by creation date, with the most recent capability appearing first.
### get,/v1/account/capabilities/{capability}
This operation retrieves information about the specified Account Capability.
### post,/v1/account/capabilities/{capability}
This operation updates an existing Account Capability.
### get,/v1/account/external_accounts
List external accounts for an account.
### post,/v1/account/external_accounts
This operation creates an external account for a given account.
### delete,/v1/account/external_accounts/{id}
This operation deletes a specified external account for a given account.
-->

View file

@ -0,0 +1,54 @@
---
id: twilio
title: Twilio
---
# Twilio
ToolJet can connect to your Twilio account to send sms.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Twilio](/img/datasource-reference/twilio/connect.png)
</div>
## Connection
- To add the Twilio datasource, click the **Datasource manager** icon on the left-sidebar of the app builder and click on the `Add datasource` button, then select **Twilio** from the modal that pops up.
- In the next dialog, you'll be asked to enter the Auth Token, Account SID, and Messaging Service SID.
- You can get the **Auth Token and Account SID** on the dashboard of your Twilio account.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Twilio](/img/datasource-reference/twilio/auth.png)
</div>
- For **Messaging Service SID**, you'll need to create a messaging service first from the Services under Messaging in the left-sidebar.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Twilio](/img/datasource-reference/twilio/sid.png)
</div>
- After entering the three credentials, you can **Save** the datasource.
## Supported operations
1. **Send message**
### Send message
This operation will send the specified message to specified mobile number.
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Twilio](/img/datasource-reference/twilio/sms.png)
</div>

View file

@ -1,17 +1,17 @@
---
id: introduction
title: Introduction
description: ToolJet is an **open-source low-code framework** to build and deploy custom internal tools. ToolJet can connect to your data sources such as databases ( PostgreSQL, MongoDB, MySQL, Elasticsearch, Firestore, DynamoDB, Redis and more ), API endpoints ( ToolJet supports OAuth2 authorization ) and external services ( Stripe, Slack, Google Sheets, Airtable and more ). Once the data sources are connected, ToolJet can run queries on these data sources to fetch and update data. The data fetched from data sources can be visualised and modified using the UI widgets such as tables, charts, forms, etc.
description: ToolJet is an **open-source low-code framework** to build and deploy custom internal tools. ToolJet can connect to your data sources such as databases ( PostgreSQL, MongoDB, MS SQL Server, Snowflake, , BigQuery, etc ), API/GraphQL endpoints, SaaS tools ( Airtable, Stripe, Google Sheets, etc ) and cloud object storage services ( AWS S3, Google Cloud Storage and Minio ). Once the data sources are connected, ToolJet can run queries on these data sources to fetch and update data. The data fetched from data sources can be visualised and modified using the UI widgets such as tables, charts, forms, etc.
slug: /
---
# Introduction
ToolJet is an **open-source low-code framework** to build and deploy custom internal tools. ToolJet can connect to your data sources such as databases ( PostgreSQL, MongoDB, MySQL, Elasticsearch, Firestore, DynamoDB, Redis and more ), API endpoints ( ToolJet supports OAuth2 authorization ) and external services ( Stripe, Slack, Google Sheets, Airtable and more ). Once the data sources are connected, ToolJet can run queries on these data sources to fetch and update data. The data fetched from data sources can be visualised and modified using the UI widgets such as tables, charts, forms, etc.
ToolJet is an **open-source low-code framework** to build and deploy custom internal tools. ToolJet can connect to your data sources such as databases ( PostgreSQL, MongoDB, MS SQL Server, Snowflake, , BigQuery, etc ), API/GraphQL endpoints, SaaS tools ( Airtable, Stripe, Google Sheets, etc ) and cloud object storage services ( AWS S3, Google Cloud Storage and Minio ). Once the data sources are connected, ToolJet can run queries on these data sources to fetch and update data. The data fetched from data sources can be visualised and modified using the UI widgets such as tables, charts, forms, etc.
<div style={{textAlign: 'center'}}>
![ToolJet - List view widget](/img/introduction/githubstar.png)
<img className="screenshot-full" src="/img/introduction/githubstar.png" />
</div>

View file

@ -0,0 +1,43 @@
---
id: digitalocean
title: DigitalOcean
---
# Deploying ToolJet on DigitalOcean
Now you can quickly deploy ToolJet using the Deploy to DigitalOcean button.
## Deploying
#### Follow the steps below to deploy ToolJet on DigitalOcean:
1. Click on the button below to start one click deployment
<div style={{textAlign: 'center'}}>
[![Deploy to DigitalOcean](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/ToolJet/ToolJet/tree/main)
</div>
2. A new tab will open up, sign-in to your DigitalOCean account. Once signed-in, the **Create App** page will open up and **Resources** will be already selected. Click on **Next** button.
<img className="screenshot-full" src="/img/setup/digitalocean/resources.png" alt="ToolJet - Deploy on DigitalOcean - Resources" />
3. Now, on **Environment Variables** page you can add new variables or edit the existing ones. Check the [environment variables here](/docs/setup/env-vars).
<img className="screenshot-full" src="/img/setup/digitalocean/env.png" alt="ToolJet - Deploy on DigitalOcean - Environment Variables" />
4. On the next page, you can change the **App name**, **Project**, and the **Region**.
<img className="screenshot-full" src="/img/setup/digitalocean/region.png" alt="ToolJet - Deploy on DigitalOcean - App name" />
5. On the last page, you'll be asked to **Review** all the app details such that we entered before such as **Resources**, **Environment Variables**, **Region**, and there will also be **Billing** section at the end. Review all the details and click the **Create Resource** button.
<img className="screenshot-full" src="/img/setup/digitalocean/review.png" alt="ToolJet - Deploy on DigitalOcean - App name" />
6. Once you click the **Create Resource** button, the build will begin. Once the build is complete, you'll see the resource and a **URL** next to it. Click on the URL to open the deployed **ToolJet**.
:::tip
ToolJet server and client can be deployed as standalone applications. If you do not want to deploy the client on DigitalOcean, modify `package.json` accordingly. We have a [guide](/docs/setup/client) on deploying ToolJet client using services such as Firebase.
:::

View file

@ -95,7 +95,7 @@ If you rather want to try out ToolJet locally with docker, you can follow the st
8. Seed the database:
```bash
docker-compose run server npm run db:seed
docker-compose exec server npm run db:seed:prod
```
This seeds the database with a default user with the following credentials:

View file

@ -13,7 +13,7 @@ Both the ToolJet server and client requires some environment variables to start
| variable | description |
| ------------ | --------------------------------------------------------------- |
| TOOLJET_HOST | the public URL of ToolJet client ( eg: https://app.tooljet.com ) |
| TOOLJET_HOST | the public URL of ToolJet client ( eg: https://app.tooljet.com ) |
#### Lockbox configuration ( required )
@ -91,8 +91,13 @@ You will still be able to see the signup page but won't be able to successfully
#### Serve client as a server end-point ( optional )
By default, the `SERVE_CLIENT` variable will be set to `false` and the server won't serve the client at its `/` end-point.
You can set `SERVE_CLIENT` to `true` and the server will attempt to serve the client at its root end-point (`/`).
By default, the `SERVE_CLIENT` variable will be unset and the server will serve the client at its `/` end-point.
You can set `SERVE_CLIENT` to `false` to disable this behaviour.
#### Serve client at subpath
If ToolJet is hosted on a domain subpath, you can set the environment variable `SUB_PATH` to support it.
Please note the subpath is to be set with trailing `/` and is applicable only when the server is serving the frontend client.
#### SMTP configuration ( optional )

View file

@ -130,7 +130,7 @@ Signing up requires [SMTP configuration](https://docs.tooljet.com/docs/setup/env
```bash
gcloud run deploy <replace-service-name> \
--image gcr.io/<replace-your-project-id>/tooljet/tooljet-server-ce:latest \
--args "npm,run,db:seed"
--args "npm,run,db:seed:prod"
```
The deployment will fail as it runs a seed script. Check logs to see that default user was created. Now run the following command to have the app deployed.

View file

@ -40,7 +40,7 @@ You will be able to access your ToolJet installation once the pods and services
If you want to seed the database with a sample user, please SSH into a pod and run:
`npm run db:seed --prefix server`
`npm run db:seed:prod --prefix server`
This seeds the database with a default user with the following credentials:

View file

@ -65,7 +65,7 @@ You will be able to access your ToolJet installation once the pods, service and
If you want to seed the database with a sample user, please SSH into a pod and run:
`npm run db:seed --prefix server`
`npm run db:seed:prod --prefix server`
This seeds the database with a default user with the following credentials:

View file

@ -1,5 +1,5 @@
---
id: organization-environment-variables
id: workspace-environment-variables
title: Workspace Environment Variables
---

View file

@ -6,11 +6,9 @@ title: Date-range picker
The date-range picker widget allows users to select a range of dates.
<div style={{textAlign: 'center'}}>
## How To Use Date Range Picker Widget
![ToolJet - Widget Reference - Date range picker](/img/widgets/date-range-picker/date-range.gif)
</div>
<iframe height="500" src="https://www.youtube.com/embed/iBJQeh1kerE" title="Date range picker Widget" frameborder="0" allowfullscreen width="100%"></iframe>
## Properties

View file

@ -6,11 +6,9 @@ title: Dropdown
The Dropdown widget can be used to collect user input from a list of options.
<div style={{textAlign: 'center'}}>
## How To Use Dropdown Widget
![ToolJet - Widget Reference - Dropdown](/img/widgets/dropdown/drop.png)
</div>
<iframe height="500" src="https://www.youtube.com/embed/PKlkD2rtlP8" title="Dropdown widget" frameborder="0" allowfullscreen width="100%"></iframe>
:::tip
Dropdown options can be referred to your query data with dynamic variables.

View file

@ -6,11 +6,9 @@ title: Radio Button
Radio button widget can be used to select one option from a group of options.
<div style={{textAlign: 'center'}}>
## How To Use Radio button Widget
![ToolJet - Widget Reference - RadioButton](/img/widgets/radio-button/radiobutton.png)
</div>
<iframe height="500" src="https://www.youtube.com/embed/lfy7nq6NqAw" title="Radio button Widget" frameborder="0" allowfullscreen width="100%"></iframe>
:::tip
Radio buttons are preferred when the list of options is less than six, and all the options can be displayed at once.

View file

@ -51,17 +51,21 @@ Toggle on or off to display the widget in mobile view. You can programmatically
## Styles
### Text color
| Style | Description |
| ----------- | ----------- |
| Font Weight | You can change the font weight of the text in following ways: **normal (default), bold, lighter, bolder** |
| Text Decoration | You can change the text decoration in following ways : **none(default), overline, line-through, underline, overline underline** |
| Text Transformation | You can transform the text in following ways: **none (default), uppercase, lowercase, capitalize** |
| Font Style | You can change the font style in following ways: **normal(default), italic, oblique** |
| Line Height | You can change the line height by providing number as input (example - 1.5) |
| Text Indent | You can change the text indent by providing the number as input (example - 10) |
| Letter Spacing | You can change the letter spacing by proviving the number as input (example - 2) |
| Word Spacing | You can change the letter spacing by proviving the number as input (example - 2) |
| Font Variant | You can change the font variant of the text in the following ways: **normal (default), small-caps, initial, inherit** |
| Text Size | By default, the text size is set to 14. You can enter any value from 1-100 to set custom text size. |
| Text Color | You can change the background color of the text by entering the Hex color code or choosing a color of your choice from the color picker. |
| Align Text | You can align the text inside the widget in following ways: left, right, center, justified |
Change the color of the text by providing the `Hex color code` or choosing a color from the picker.
### Align text
You can align the text inside the widget in following ways:
- Left
- Right
- Center
- Justified
### Visibility

View file

@ -10,11 +10,9 @@ Textarea widgets let users enter and edit just text like [Text Input](/docs/widg
Textarea should be preferred over [Text Input](/docs/widgets/text-input) when user input is more than one sentence.
:::
<div style={{textAlign: 'center'}}>
## How To Use Textarea Widget
![ToolJet - Widget Reference - Text input](/img/widgets/textarea/textarea.png)
</div>
<iframe height="500" src="https://www.youtube.com/embed/ja66x6DeZxk" title="Textarea Widget" frameborder="0" allowfullscreen width="100%"></iframe>
## Properties

View file

@ -1,3 +1,5 @@
const devServerPlugin = require('./src/plugins/devServer/index.js');
const isProd = process.env.NODE_ENV === 'production';
/** @type {import('@docusaurus/types').DocusaurusConfig} */
@ -22,16 +24,7 @@ module.exports = {
isCloseable: true,
},
colorMode: {
switchConfig: {
darkIcon: '\00a0 ',
lightIcon: '\00a0',
darkIconStyle: {
display: 'none',
},
lightIconStyle: {
display: 'none',
},
},
},
navbar: {
logo: {
@ -47,18 +40,21 @@ module.exports = {
},
{
href: 'https://github.com/ToolJet/ToolJet',
label: 'GitHub',
position: 'right',
className: 'navbar-social-link navbar-github-logo',
'aria-label': 'GitHub repository',
},
{
href: 'https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg',
label: 'Slack',
position: 'right',
className: 'navbar-social-link navbar-slack-logo',
'aria-label': 'Slack workspace',
},
{
href: 'https://twitter.com/ToolJet',
label: 'Twitter',
position: 'right',
className: 'navbar-social-link navbar-twitter-logo',
'aria-label': 'Twitter account',
},
],
},
@ -78,8 +74,8 @@ module.exports = {
title: 'Community',
items: [
{
label: 'Stack Overflow',
href: 'https://stackoverflow.com/questions/tagged/tooljet',
label: 'Slack',
href: 'https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg',
},
],
},
@ -91,8 +87,8 @@ module.exports = {
href: 'https://github.com/ToolJet/ToolJet',
},
{
label: 'Slack',
href: 'https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg',
label: 'YouTube',
href: 'https://www.youtube.com/channel/UCf1p2G5Z7fPpvlBPf4l2I1w',
},
{
label: 'Twitter',
@ -135,4 +131,7 @@ module.exports = {
},
],
],
plugins: [
devServerPlugin,
],
};

20822
docs/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,10 +14,10 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-beta.15",
"@docusaurus/plugin-google-gtag": "^2.0.0-alpha.73",
"@docusaurus/plugin-sitemap": "^2.0.0-beta.20",
"@docusaurus/preset-classic": "^2.0.0-beta.15",
"@docusaurus/core": "^2.0.1",
"@docusaurus/plugin-google-gtag": "^2.0.1",
"@docusaurus/plugin-sitemap": "^2.0.1",
"@docusaurus/preset-classic": "^2.0.1",
"@mdx-js/react": "^1.6.21",
"clsx": "^1.1.1",
"react": "^17.0.1",

View file

@ -17,6 +17,7 @@ const sidebars = {
link: {type: 'doc', id: 'setup/index'},
items: [
'setup/try-tooljet',
'setup/digitalocean',
'setup/docker',
'setup/heroku',
'setup/ec2',
@ -55,7 +56,7 @@ const sidebars = {
'tutorial/manage-users-groups',
'tutorial/keyboard-shortcuts',
'tutorial/multiworkspace',
'tutorial/organization-environment-variables'
'tutorial/workspace-environment-variables'
],
},
{
@ -92,8 +93,11 @@ const sidebars = {
'data-sources/rethinkdb',
'data-sources/saphana',
'data-sources/sendgrid',
'data-sources/slack',
'data-sources/smtp',
'data-sources/snowflake',
'data-sources/stripe',
'data-sources/twilio',
'data-sources/typesense',
'data-sources/woocommerce',
],
@ -160,8 +164,20 @@ const sidebars = {
keywords: ['actions','events'],
},
items: [
'actions/generate-file',
'actions/show-alert',
'actions/logout',
'actions/run-query',
'actions/open-webpage',
'actions/go-to-app',
'actions/show-modal',
'actions/close-modal',
'actions/copy-to-clipboard',
'actions/set-localstorage',
'actions/generate-file',
'actions/set-table-page',
'actions/set-variable',
'actions/unset-variable',
'actions/control-component',
],
},
{
@ -188,6 +204,7 @@ const sidebars = {
label: 'Enterprise',
items: [
'Enterprise/audit_logs',
'Enterprise/white-label',
]
},
{

View file

@ -36,8 +36,8 @@
--tblr-danger: #d63939;
--tblr-light: #f4f6fa;
--tblr-dark: #232e3c;
--tblr-font-sans-serif: "Inter",-apple-system,BlinkMacSystemFont,San Francisco,Segoe UI,Roboto,Helvetica Neue,sans-serif;
--tblr-font-monospace: null,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;
--tblr-font-sans-serif: "Inter", -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
--tblr-font-monospace: null, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
--tblr-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
}
@ -62,13 +62,13 @@
margin-bottom: 1rem;
border-radius: 4px;
background: #fff;
border: 1px solid rgba(101,109,119,.16);
box-shadow: rgba(35,46,60,.04) 0 2px 4px 0;
border: 1px solid rgba(101, 109, 119, .16);
box-shadow: rgba(35, 46, 60, .04) 0 2px 4px 0;
border-left: .25rem solid var(--tblr-alert-color);
}
.admonition-heading {
display: none;
display: none;
}
.admonition-tip {
@ -79,7 +79,7 @@
--tblr-alert-color: #4299e1;
}
.screenshot-full {
.screenshot-full {
border: 1px solid #dadde1;
}
@ -90,7 +90,7 @@
padding: 0 var(--ifm-pre-padding);
}
.navbar__link {
.navbar__link {
color: var(--ifm-color-primary);
}
@ -113,6 +113,7 @@ body {
:root {
--ifm-font-size-base: 18px;
}
article header h1 {
font-size: 1.5rem !important;
}
@ -120,6 +121,7 @@ body {
article h2 {
font-size: 1.2rem !important;
}
.hero .hero__title {
font-size: 2.5rem;
}
@ -129,11 +131,12 @@ body {
:root {
--ifm-font-size-base: 17px;
}
article h1 {
article h1 {
font-size: 2rem !important;
}
article h2 {
article h2 {
font-size: 1.5rem !important;
}
}
@ -156,7 +159,7 @@ body {
height: 25px;
}
strong {
strong {
color: #4d72fa;
}
@ -172,6 +175,42 @@ strong {
overflow-y: auto;
}
.navbar-social-link:hover {
opacity: 0.6;
}
.navbar-social-link::before {
content: '';
width: 24px;
height: 24px;
display: flex;
background-repeat: no-repeat;
}
.navbar-github-logo::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-hidden='true' viewBox='0 0 16 16' version='1.1' data-view-component='true' %3E%3Cpath fill-rule='evenodd' d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z' %3E%3C/path%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-github-logo::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='white' aria-hidden='true' viewBox='0 0 16 16' version='1.1' data-view-component='true' %3E%3Cpath fill-rule='evenodd' d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z' %3E%3C/path%3E%3C/svg%3E");
}
.navbar-slack-logo::before {
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z' /%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-slack-logo::before {
background-image: url("data:image/svg+xml,%3Csvg fill='white' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z' /%3E%3C/svg%3E");
}
.navbar-twitter-logo::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' aria-hidden='true'%3E%3Cpath d='M23.643 4.937c-.835.37-1.732.62-2.675.733.962-.576 1.7-1.49 2.048-2.578-.9.534-1.897.922-2.958 1.13-.85-.904-2.06-1.47-3.4-1.47-2.572 0-4.658 2.086-4.658 4.66 0 .364.042.718.12 1.06-3.873-.195-7.304-2.05-9.602-4.868-.4.69-.63 1.49-.63 2.342 0 1.616.823 3.043 2.072 3.878-.764-.025-1.482-.234-2.11-.583v.06c0 2.257 1.605 4.14 3.737 4.568-.392.106-.803.162-1.227.162-.3 0-.593-.028-.877-.082.593 1.85 2.313 3.198 4.352 3.234-1.595 1.25-3.604 1.995-5.786 1.995-.376 0-.747-.022-1.112-.065 2.062 1.323 4.51 2.093 7.14 2.093 8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602.91-.658 1.7-1.477 2.323-2.41z' %3E%3C/path%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-twitter-logo::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='white' viewBox='0 0 24 24' aria-hidden='true'%3E%3Cpath d='M23.643 4.937c-.835.37-1.732.62-2.675.733.962-.576 1.7-1.49 2.048-2.578-.9.534-1.897.922-2.958 1.13-.85-.904-2.06-1.47-3.4-1.47-2.572 0-4.658 2.086-4.658 4.66 0 .364.042.718.12 1.06-3.873-.195-7.304-2.05-9.602-4.868-.4.69-.63 1.49-.63 2.342 0 1.616.823 3.043 2.072 3.878-.764-.025-1.482-.234-2.11-.583v.06c0 2.257 1.605 4.14 3.737 4.568-.392.106-.803.162-1.227.162-.3 0-.593-.028-.877-.082.593 1.85 2.313 3.198 4.352 3.234-1.595 1.25-3.604 1.995-5.786 1.995-.376 0-.747-.022-1.112-.065 2.062 1.323 4.51 2.093 7.14 2.093 8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602.91-.658 1.7-1.477 2.323-2.41z' %3E%3C/path%3E%3C/svg%3E");
}
[data-theme='light'] .DocSearch {
/* --docsearch-primary-color: var(--ifm-color-primary); */
/* --docsearch-text-color: var(--ifm-font-color-base); */
@ -205,9 +244,14 @@ strong {
--docsearch-hit-background: var(--ifm-color-emphasis-100);
/* Footer */
--docsearch-footer-background: var(--ifm-background-surface-color);
--docsearch-key-gradient: linear-gradient(
-26.5deg,
var(--ifm-color-emphasis-200) 0%,
var(--ifm-color-emphasis-100) 100%
);
}
--docsearch-key-gradient: linear-gradient(-26.5deg,
var(--ifm-color-emphasis-200) 0%,
var(--ifm-color-emphasis-100) 100%);
}
@media screen and (min-width: 768px) {
.DocSearch-Button-Container {
min-width: 200px;
/* Fixes #3856 */
}
}

View file

@ -0,0 +1,12 @@
module.exports = function (context, options) {
return {
name: 'dev-server-plugin',
configureWebpack(config, isServer, utils) {
return {
devServer: {
open: '/docs',
},
};
},
};
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

72
frontend/.eslintrc.js Normal file
View file

@ -0,0 +1,72 @@
module.exports = {
env: {
browser: true,
amd: true,
es2021: true,
node: true,
'jest/globals': true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:prettier/recommended',
],
parser: '@babel/eslint-parser',
parserOptions: {
requireConfigFile: false,
babelOptions: {
configFile: __dirname + '/babel.config.js',
},
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['react', 'prettier', 'jest'],
rules: {
'prettier/prettier': [
'error',
{
semi: true,
trailingComma: 'es5',
printWidth: 120,
singleQuote: true,
arrowParens: 'always',
proseWrap: 'preserve',
},
],
'react/prop-types': 0,
'react/display-name': 'off',
'no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'react/no-deprecated': 0,
'no-prototype-builtins': 0,
'jest/no-disabled-tests': 'warn',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'warn',
'jest/valid-expect': 'error',
},
settings: {
react: {
version: 'detect',
},
'import/resolver': 'webpack',
},
globals: {
path: true,
fetch: true,
process: true,
module: true,
__dirname: true,
},
};

View file

@ -1,63 +0,0 @@
{
"env": {
"browser": true,
"amd": true,
"es2021": true,
"jest/globals": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:prettier/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": ["react", "prettier", "jest"],
"rules": {
"prettier/prettier": [
"error",
{
"semi": true,
"trailingComma": "es5",
"printWidth": 120,
"singleQuote": true,
"arrowParens": "always",
"proseWrap": "preserve"
}
],
"react/prop-types": 0,
"react/display-name": "off",
"no-unused-vars": ["warn", {
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}],
"react/no-deprecated": 0,
"no-prototype-builtins": 0,
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
},
"settings": {
"react": {
"version": "detect"
},
"import/resolver": "webpack"
},
"globals": {
"fetch": true,
"process": true,
"module": true,
"__dirname": true
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,48 +3,29 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/core": "^7.4.3",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.16.10",
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.0.0",
"@react-google-maps/api": "^2.1.1",
"@sentry/react": "^6.17.6",
"@sentry/tracing": "^6.17.6",
"@svgr/webpack": "^5.5.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"@tooljet/plugins": "../plugins",
"@uiw/react-codemirror": "^3.0.6",
"@y-presence/react": "^2.0.0",
"array-move": "^3.0.1",
"axios": "^0.24.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.0.5",
"babel-plugin-console-source": "^2.0.5",
"babel-plugin-import": "^1.13.3",
"bootstrap": "^4.6.0",
"classnames": "^2.3.1",
"compression-webpack-plugin": "^10.0.0",
"css-loader": "^6.5.1",
"date-fns": "^2.28.0",
"dompurify": "^2.2.7",
"draft-js": "^0.11.7",
"draft-js-export-html": "^1.4.1",
"driver.js": "^0.9.8",
"emoji-mart": "^3.0.1",
"esbuild": "^0.15.3",
"fuse.js": "^6.4.6",
"history": "^4.9.0",
"html-loader": "^3.1.0",
"html-webpack-plugin": "^5.3.2",
"immer": "^9.0.6",
"immutability-helper": "^3.1.1",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"node-sass": "^4.14.1",
"papaparse": "^5.3.0",
"plotly.js-basic-dist-min": "^1.58.4",
"psl": "^1.8.0",
@ -90,21 +71,29 @@
"react-tooltip": "^4.2.18",
"react-zoom-pan-pinch": "^2.1.3",
"rxjs": "^6.3.3",
"sass-loader": "^12.4.0",
"semver": "^5.7.1",
"style-loader": "^3.3.1",
"superstruct": "^0.15.4",
"terser-webpack-plugin": "^5.3.4",
"tinycolor2": "^1.4.2",
"url-join": "^5.0.0",
"uuid": "8.3.2",
"webpack": "^5.55.1",
"webpack-cli": "^4.8.0",
"xlsx": "^0.18.5",
"y-websocket": "^1.4.0",
"yjs": "^13.5.28",
"yup": "^0.27.0"
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/eslint-parser": "^7.18.9",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.16.10",
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.18.6",
"babel-loader": "^8.0.5",
"babel-plugin-console-source": "^2.0.5",
"babel-plugin-import": "^1.13.3",
"compression-webpack-plugin": "^10.0.0",
"css-loader": "^6.5.1",
"esbuild": "^0.15.3",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-webpack": "^0.13.1",
@ -113,10 +102,22 @@
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-react": "^7.25.2",
"eslint-plugin-react-hooks": "^4.2.0",
"html-loader": "^3.1.0",
"html-webpack-plugin": "^5.3.2",
"jest": "^27.5.1",
"node-sass": "^4.14.1",
"path": "^0.12.7",
"prettier": "^2.3.2",
"webpack-dev-server": "^4.7.4"
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.3.4",
"webpack": "^5.55.1",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.7.4",
"@svgr/webpack": "^5.5.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1"
},
"scripts": {
"start": "webpack serve --port 8082 --host 0.0.0.0",
@ -155,4 +156,4 @@
"jsx"
]
}
}
}

View file

@ -21,7 +21,7 @@ import { ResetPassword } from '@/ResetPassword';
import { ManageSSO } from '@/ManageSSO';
import { ManageOrgVars } from '@/ManageOrgVars';
import { lt } from 'semver';
import { Toaster } from 'react-hot-toast';
import Toast from '@/_ui/Toast';
import { RealtimeEditor } from '@/Editor/RealtimeEditor';
import { Editor } from '@/Editor/Editor';
import { RedirectSso } from '@/RedirectSso/RedirectSso';
@ -76,6 +76,7 @@ class App extends React.Component {
if (darkMode) {
toastOptions = {
className: 'toast-dark-mode',
style: {
borderRadius: '10px',
background: '#333',
@ -259,7 +260,7 @@ class App extends React.Component {
/>
</div>
</BrowserRouter>
<Toaster toastOptions={toastOptions} />
<Toast toastOptions={toastOptions} />
</>
);
}

View file

@ -164,9 +164,6 @@ export const Box = function Box({
? validateProperties(resolvedProperties, componentMeta.properties)
: [resolvedProperties, []];
if (componentMeta.component === 'Button') {
// console.log('validated properties', validatedProperties, 'resolvedProperties', resolvedProperties);
}
const resolvedStyles = resolveStyles(component, currentState, null, customResolvables);
const [validatedStyles, styleErrors] =
mode === 'edit' && component.validate

View file

@ -9,6 +9,7 @@ export const Number = ({ value, onChange, forceCodeBox }) => {
backgroundColor: 'transparent',
border: 'none',
color: darkMode && '#fff',
width: '100%',
};
return (
<>
@ -17,7 +18,7 @@ export const Number = ({ value, onChange, forceCodeBox }) => {
<div className="field form-control" style={{ padding: '0.225rem 0.35rem' }} data-cy="border-radius-input">
<input
style={numberTheme}
type="text"
type="number"
onChange={(e) => {
setNumber(e.target.value);
onChange(`{{${e.target.value}}}`);

View file

@ -5,7 +5,14 @@ import 'react-dates/lib/css/_datepicker.css';
import 'react-dates/initialize';
import moment from 'moment';
export const DaterangePicker = function DaterangePicker({ height, properties, styles, setExposedVariable, width }) {
export const DaterangePicker = function DaterangePicker({
height,
properties,
styles,
setExposedVariable,
width,
darkMode,
}) {
const { borderRadius, visibility, disabledState } = styles;
const { defaultStartDate, defaultEndDate } = properties;
const formatProp = typeof properties.format === 'string' ? properties.format : '';
@ -54,7 +61,10 @@ export const DaterangePicker = function DaterangePicker({ height, properties, st
}
return (
<div className="daterange-picker-widget p-0" style={{ height, display: visibility ? '' : 'none' }}>
<div
className={`daterange-picker-widget ${darkMode && 'theme-dark'} p-0`}
style={{ height, display: visibility ? '' : 'none' }}
>
<DateRangePicker
disabled={disabledState}
startDate={startDate}

View file

@ -2,6 +2,7 @@ import React, { useEffect, useMemo } from 'react';
import { useDropzone } from 'react-dropzone';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
import { toast } from 'react-hot-toast';
import * as XLSX from 'xlsx/xlsx.mjs';
export const FilePicker = ({
width,
@ -24,9 +25,11 @@ export const FilePicker = ({
const fileType = component.definition.properties.fileType?.value ?? 'image/*';
const maxSize = component.definition.properties.maxSize?.value ?? 1048576;
const minSize = component.definition.properties.minSize?.value ?? 0;
const parseContent = component.definition.properties.parseContent?.value ?? false;
const parseContent = resolveWidgetFieldValue(
component.definition.properties.parseContent?.value ?? false,
currentState
);
const fileTypeFromExtension = component.definition.properties.parseFileType?.value ?? 'auto-detect';
const parsedEnableDropzone =
typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone, currentState) : true;
const parsedEnablePicker =
@ -182,7 +185,9 @@ export const FilePicker = ({
content: readFileAsText,
dataURL: readFileAsDataURL, // TODO: Fix dataURL to have correct format
base64Data: readFileAsDataURL,
parsedData: shouldProcessFileParsing ? await processFileContent(file.type, readFileAsText) : null,
parsedData: shouldProcessFileParsing
? await processFileContent(file.type, { readFileAsDataURL, readFileAsText })
: null,
filePath: file.path,
};
};
@ -395,11 +400,27 @@ const processCSV = (str, delimiter = ',') => {
handleErrors(error);
}
};
const processXls = (str) => {
try {
const wb = XLSX.read(str, { type: 'base64' });
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
/* Convert array of arrays */
const data = XLSX.utils.sheet_to_json(ws);
return data;
} catch (error) {
console.log(error);
handleErrors(error);
}
};
const processFileContent = (fileType, fileContent) => {
switch (fileType) {
case 'text/csv':
return processCSV(fileContent);
return processCSV(fileContent.readFileAsText);
case 'application/vnd.ms-excel':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
return processXls(fileContent.readFileAsDataURL);
default:
break;

View file

@ -58,6 +58,7 @@ export const Modal = function Modal({
enforceFocus={false}
animation={false}
onEscapeKeyDown={() => hideOnEsc && hideModal()}
id="modal-container"
>
{containerProps.mode === 'edit' && <ConfigHandle id={id} component={component} />}
{!hideTitleBar && (

View file

@ -122,7 +122,7 @@ export const Multiselect = function Multiselect({
<div className="col-auto my-auto d-flex align-items-center">
<label
style={{ marginRight: label ? '1rem' : '', marginBottom: 0 }}
className="form-label py-1"
className="form-label py-1 text-secondary"
data-cy={`multiselect-label-${component.name.toLowerCase()}`}
>
{label}

View file

@ -1,6 +1,8 @@
import React, { useState, useCallback, useRef, useEffect } from 'react';
// eslint-disable-next-line import/no-unresolved
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import { debounce } from 'lodash';
export const PDF = React.memo(({ styles, properties, width, height, component }) => {
const pdfName = component.name;
@ -9,30 +11,39 @@ export const PDF = React.memo(({ styles, properties, width, height, component })
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(null);
const pageRef = useRef([]);
const documentRef = useRef(null);
const hasScrollRef = useRef(false);
const [error, setError] = useState(true);
const [pageLoading, setPageLoading] = useState(true);
const onDocumentLoadSuccess = (document) => {
const [hasButtonClicked, setButtonClick] = useState(false);
const onDocumentLoadSuccess = async (document) => {
const { numPages: nextNumPages } = document;
setNumPages(nextNumPages);
setPageNumber(1);
setError(false);
setPageLoading(false);
};
const onDocumentLoadError = () => {
setError(true);
};
useEffect(() => {
setPageLoading(true);
}, [url]);
const options = {
root: document.querySelector('#pdf-wrapper'),
rootMargin: '0px',
threshold: 0.5,
threshold: 0.7,
};
const trackIntersection = (entries) => {
let isCaptured = false;
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (entry.isIntersecting && !isCaptured && hasScrollRef.current) {
isCaptured = true;
const currentPage = parseInt(entry.target.getAttribute('data-page-number'));
if (pageNumber !== currentPage) setPageNumber(currentPage);
}
@ -50,7 +61,9 @@ export const PDF = React.memo(({ styles, properties, width, height, component })
const updatePage = useCallback(
(offset) => {
pageRef.current[pageNumber + offset - 1].scrollIntoView({ block: 'nearest' });
const { offsetTop } = pageRef.current[pageNumber + offset - 1];
documentRef.current.scrollTop = offsetTop;
setButtonClick(true);
setPageNumber((prevPageNumber) => (prevPageNumber || 1) + offset);
},
[pageNumber]
@ -99,10 +112,24 @@ export const PDF = React.memo(({ styles, properties, width, height, component })
document.body.removeChild(anchor);
URL.revokeObjectURL(pdfURL);
}
const handleScroll = () => {
if (hasButtonClicked) return setButtonClick(false);
if (!hasScrollRef.current) hasScrollRef.current = true;
debounce(() => {
if (hasScrollRef.current) hasScrollRef.current = false;
}, 150);
};
return (
<div style={{ display: visibility ? 'flex' : 'none', width: width - 3, height }}>
<div className="d-flex position-relative h-100 flex-column" style={{ margin: '0 auto', overflow: 'hidden' }}>
<div className="scrollable h-100 col position-relative" id="pdf-wrapper">
<div
className="scrollable h-100 col position-relative"
id="pdf-wrapper"
ref={documentRef}
onScroll={handleScroll}
>
{url === '' ? 'No PDF file specified' : renderPDF()}
</div>
{!error && !pageLoading && (showDownloadOption || pageControls) && (

View file

@ -2,8 +2,22 @@ import React, { useState, useEffect } from 'react';
import DOMPurify from 'dompurify';
export const Text = function Text({ height, properties, styles, darkMode, registerAction }) {
const { textSize, textColor, textAlign, visibility, disabledState } = styles;
let {
textSize,
textColor,
textAlign,
visibility,
disabledState,
fontWeight,
decoration,
transformation,
fontStyle,
lineHeight,
textIndent,
letterSpacing,
wordSpacing,
fontVariant,
} = styles;
const [loadingState, setLoadingState] = useState(false);
const [text, setText] = useState(() => computeText());
@ -31,6 +45,15 @@ export const Text = function Text({ height, properties, styles, darkMode, regist
display: visibility ? 'flex' : 'none',
alignItems: 'center',
textAlign,
fontWeight: fontWeight ? fontWeight : fontWeight === '0' ? 0 : 'normal',
lineHeight: lineHeight ?? 1.5,
textDecoration: decoration ?? 'none',
textTransform: transformation ?? 'none',
fontStyle: fontStyle ?? 'none',
fontVariant: fontVariant ?? 'normal',
textIndent: `${textIndent}px` ?? '0px',
letterSpacing: `${letterSpacing}px` ?? '0px',
wordSpacing: `${wordSpacing}px` ?? '0px',
};
return (

Some files were not shown because too many files have changed in this diff Show more