Merge pull request #5955 from ToolJet/release/v2.4.0

Release v2.4.0
This commit is contained in:
Midhun G S 2023-04-10 16:48:06 +05:30 committed by GitHub
commit fe7766db50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
639 changed files with 105869 additions and 4065 deletions

View file

@ -26,9 +26,6 @@ spec:
- key: DISABLE_SIGNUPS
value: "false"
scope: RUN_TIME
- key: DISABLE_MULTI_WORKSPACE
value: "false"
scope: RUN_TIME
- key: DEPLOYMENT_PLATFORM
value: "digitalocean"
- key: DATABASE_URL

View file

@ -52,8 +52,6 @@ SMTP_PORT=
# DISABLE USER SIGNUPS (true or false). only applicable if Multi-Workspace feature is enabled
DISABLE_SIGNUPS=
# Disable Multi-Workspace features (true or false)
DISABLE_MULTI_WORKSPACE=
# OBSERVABILITY
APM_VENDOR=
@ -76,3 +74,6 @@ SSO_DISABLE_SIGNUPS=
#ONBOARDING
ENABLE_ONBOARDING_QUESTIONS_FOR_ALL_SIGN_UPS=
#session expiry in minutes
USER_SESSION_EXPIRY=2880

View file

@ -14,7 +14,7 @@ body:
⚠ Verify first that your issue is not [already reported on GitHub][issue search].
**Tip:** If you are seeking community support, please consider [joining Slack community][Slack community].
[Slack community]: https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg
[Slack community]: https://tooljet.com/slack
[issue search]: ../search?q=is%3Aissue&type=issues

View file

@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: ❓ Get Support from Slack Community
url: https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg
about: Please ask and answer questions here. 🏥
url: https://tooljet.com/slack
about: Please ask and answer questions here. 🏥

View file

@ -1 +1 @@
2.3.0
2.4.0

View file

@ -52,4 +52,4 @@ We use GitHub issues to track public bugs. Report a bug by [opening a new issue]
By contributing, you agree that your contributions will be licensed under its AGPL v3 License.
## Questions?
Contact us on [Slack](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) or mail us at [hello@tooljet.io](mailto:hello@tooljet.io).
Contact us on [Slack](https://tooljet.com/slack) or mail us at [hello@tooljet.io](mailto:hello@tooljet.io).

View file

@ -119,7 +119,7 @@ You can use ToolJet cloud for a fully managed solution. If you want to self-host
## Community support
For general help using ToolJet, please refer to the official [documentation](https://docs.tooljet.com/docs/). For additional help, you can use one of these channels to ask a question:
- [Slack](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) - Discussions with the community and the team.
- [Slack](https://tooljet.com/slack) - Discussions with the community and the team.
- [GitHub](https://github.com/ToolJet/ToolJet/issues) - For bug reports and feature requests.
- [Twitter](https://twitter.com/ToolJet) - Get the product updates easily.

View file

@ -37,10 +37,6 @@
"description": "Disable sign up in login page only applicable if Multi-Workspace feature is turned on",
"value": "false"
},
"DISABLE_MULTI_WORKSPACE": {
"description": "Disables Multi-Workspace feature",
"value": "false"
},
"ENABLE_TOOLJET_DB": {
"description": "To enable Tooljet Database feature",
"value": "false"

View file

@ -90,4 +90,14 @@ export const sortSelectors = {
selectOrderField: '[data-cy="select-order-field"]',
deleteIcon: '[data-cy="delete-icon"]',
addConditionLink: '[data-cy="add-another-condition-link"]',
};
export const editRowSelectors = {
editRowbutton: '[data-cy="edit-row-button-text"]',
editRowHeader: '[data-cy="edit-row-header"]',
idColumnNameLabel: '[data-cy="id-column-name-label"]',
selectRowDropdown: '[data-cy="select-row-dropdown"]',
getRowData: (rowNumber, columnName) => {
return `[data-cy="id-${String(rowNumber).toLowerCase().replace(/\s+/g, "-")}-column-${String(columnName).toLowerCase().replace(/\s+/g, "-")}-table-cell"]`
}
};

View file

@ -16,6 +16,7 @@ export const databaseText = {
idColumnHeader: "id",
noRecordsText: "You don't have any records yet.",
deleteRecordButton: "Delete records",
idColumnName: "id",
tableCreatedSuccessfullyToast: (tableName) => {
return `${tableName} created successfully`;
@ -31,6 +32,9 @@ export const databaseText = {
},
deleteRowToast: (tableName, rowNumber) => {
return `Deleted ${rowNumber} rows from table "${tableName}"`
},
invalidErrorText: (value) => {
return `invalid input syntax for type integer: "${value}"`
}
};
@ -73,3 +77,7 @@ export const sortText = {
descending: "Descending"
}
}
export const editRowText = {
editRowHeader: "Edit a row",
selectRowToEditText: "Select a row to edit"
}

View file

@ -1,8 +1,10 @@
import { filterSelectors, sortSelectors } from "Selectors/database";
import { filterText, sortText } from "Texts/database";
import { databaseText, filterText, sortText } from "Texts/database";
import { navigateToDatabase } from "Support/utils/common";
import {
verifyAllElementsOfPage, createTableAndVerifyToastMessage, editTableNameAndVerifyToastMessage,
verifyAllElementsOfPage,
createTableAndVerifyToastMessage,
editTableNameAndVerifyToastMessage,
deleteTableAndVerifyToastMessage,
createNewColumnAndVerify,
navigateToTable,
@ -10,7 +12,9 @@ import {
filterOperation,
sortOperation,
deleteCondition,
deleteRowAndVerify
deleteRowAndVerify,
editRowWithInvalidData,
editRowAndVerify,
} from "Support/utils/database";
import { fake } from "Fixtures/fake";
import { randomNumber } from "Support/utils/commonWidget";
@ -26,22 +30,28 @@ describe("Database Functionality", () => {
const columnDetails = () => {
let column = {
name: fake.firstName,
defaultValueDoublePrecision: Math.floor(Math.random() * (1000 - 100) + 100) / 100,
defaultValueDoublePrecision:
Math.floor(Math.random() * (1000 - 100) + 100) / 100,
defaultValueInt: randomNumber(10, 99),
defaultValueVarchar: randomString(data.maximumLength)
defaultValueVarchar: randomString(data.maximumLength),
};
return column;
};
let column1 = columnDetails();
let column2 = columnDetails();
let column3 = columnDetails();
let column4 = columnDetails();
let rowData = {
varcharData: randomString(data.maximumLength),
doublePrecisionData: Math.floor(Math.random() * (1000 - 100) + 100) / 100,
intData: randomNumber(10, 99),
const rowData = () => {
let row = {
varcharData: randomString(data.maximumLength),
doublePrecisionData: Math.floor(Math.random() * (1000 - 100) + 100) / 100,
intData: randomNumber(10, 99),
};
return row;
};
let row1 = rowData();
let row2 = rowData();
let row3 = rowData();
let row4 = rowData();
beforeEach(() => {
cy.appUILogin();
@ -50,24 +60,78 @@ describe("Database Functionality", () => {
navigateToDatabase();
verifyAllElementsOfPage();
createTableAndVerifyToastMessage(data.tableName, false);
createTableAndVerifyToastMessage(data.newTableName, true, [column1.name, column2.name], [data.dataType[0], data.dataType[1]], true, [column1.defaultValueVarchar, column1.defaultValueInt]);
createTableAndVerifyToastMessage(
data.newTableName,
true,
[column1.name, column2.name],
[data.dataType[0], data.dataType[1]],
true,
[column1.defaultValueVarchar, column1.defaultValueInt]
);
});
it("Verify all operations of table", () => {
navigateToDatabase();
navigateToTable(data.tableName);
editTableNameAndVerifyToastMessage(data.newTableName, data.editTableName);
deleteTableAndVerifyToastMessage(data.editTableName);
createNewColumnAndVerify(data.tableName, column1.name, data.dataType[0], true, column1.defaultValueVarchar);
addNewRowAndVerify(data.tableName, false)
addNewRowAndVerify(data.tableName, false, [column1.name], true, [rowData.varcharData])
createNewColumnAndVerify(data.tableName, column2.name, data.dataType[1], false);
addNewRowAndVerify(data.tableName, false, [column1.name, column2.name], [rowData.varcharData, rowData.intData]);
addNewRowAndVerify(data.tableName, true, [column1.name, column2.name], [rowData.varcharData, rowData.intData]);
filterOperation(data.tableName, ["id"], [filterText.operation.greaterThan], ["2"]);
deleteCondition(filterSelectors.filterButton, ["id"], filterSelectors.deleteIcon)
sortOperation(data.tableName, ["id"], [sortText.order.descending])
deleteCondition(sortSelectors.sortButton, ["id"], sortSelectors.deleteIcon)
createNewColumnAndVerify(
data.tableName,
column1.name,
data.dataType[0],
true,
column1.defaultValueVarchar
);
addNewRowAndVerify(data.tableName, false);
addNewRowAndVerify(data.tableName, false, [column1.name], true, [
row1.varcharData,
]);
createNewColumnAndVerify(
data.tableName,
column2.name,
data.dataType[1],
false
);
addNewRowAndVerify(
data.tableName,
false,
[column1.name, column2.name],
[row2.varcharData, row2.intData]
);
addNewRowAndVerify(
data.tableName,
true,
[column1.name, column2.name],
[row3.varcharData, row3.intData]
);
filterOperation(
data.tableName,
[databaseText.idColumnName],
[filterText.operation.greaterThan],
["2"]
);
deleteCondition(
filterSelectors.filterButton,
[databaseText.idColumnName],
filterSelectors.deleteIcon
);
sortOperation(
data.tableName,
[databaseText.idColumnName],
[sortText.order.descending]
);
deleteCondition(
sortSelectors.sortButton,
[databaseText.idColumnName],
sortSelectors.deleteIcon
);
cy.reload();
deleteRowAndVerify(data.tableName, ["1", "2"]);
editRowWithInvalidData(data.tableName, "3", column2.name, row4.varcharData);
editRowAndVerify(
data.tableName,
"3",
[databaseText.idColumnName, column1.name, column2.name],
[row4.varcharData, row4.intData]
);
});
});

View file

@ -112,6 +112,40 @@ describe("Self host onboarding", () => {
signup.verifyandModifySizeOftheCompany();
cy.get(commonSelectors.pageLogo).should("be.visible");
cy.get(commonSelectors.setUpadminCheckPoint).verifyVisibleElement(
"have.text",
commonText.setUpadminCheckPoint
);
cy.get(commonSelectors.setUpworkspaceCheckPoint).verifyVisibleElement(
"have.text",
commonText.setUpworkspaceCheckPoint
);
cy.get(commonSelectors.companyProfileCheckPoint).verifyVisibleElement(
"have.text",
commonText.companyProfileCheckPoint
);
cy.get(commonSelectors.onboardingPageSubHeader).verifyVisibleElement(
"have.text",
commonText.onboardingPageSubHeader
);
cy.get(commonSelectors.continueButton).verifyVisibleElement(
"have.text",
commonText.continueButton
);
signup.commonElementsWorkspaceSetup();
cy.get(commonSelectors.onboardingPageHeader).verifyVisibleElement(
"have.text",
"Enter your phone number"
);
cy.get(".form-control").should("be.visible");
cy.get(".tj-onboarding-phone-input-wrapper")
.find("input")
.type("919876543210");
cy.get(commonSelectors.continueButton).click();
cy.get(commonSelectors.workspaceName).verifyVisibleElement(
"have.text",
"My workspace"

View file

@ -1,5 +1,17 @@
import { databaseSelectors, createNewColumnSelectors, createNewRowSelectors, filterSelectors, sortSelectors } from "Selectors/database";
import { databaseText, createNewColumnText, createNewRowText, filterText, sortText } from "Texts/database";
import {
databaseSelectors,
createNewColumnSelectors,
createNewRowSelectors,
filterSelectors,
sortSelectors,
editRowSelectors,
} from "Selectors/database";
import {
databaseText,
createNewColumnText,
createNewRowText,
editRowText,
} from "Texts/database";
import { commonSelectors } from "Selectors/common";
import { commonText } from "Texts/common";
@ -18,17 +30,33 @@ export const verifyAllElementsOfPage = () => {
cy.get(databaseSelectors.allTableSubheader).should("be.visible");
};
export const navigateToTable = (tableName) => {
cy.get(databaseSelectors.currentTable(tableName)).scrollIntoView().should("be.visible")
cy.get(databaseSelectors.currentTableName(tableName)).verifyVisibleElement("have.text", tableName).click();
cy.get(databaseSelectors.currentTable(tableName))
.scrollIntoView()
.should("be.visible");
cy.get(databaseSelectors.currentTableName(tableName))
.verifyVisibleElement("have.text", tableName)
.realClick();
};
export const createTableAndVerifyToastMessage = (tableName, columnDetails = true, columnName = [], columnDataType = [], defaultValue, columnDefaultValue = []) => {
export const createTableAndVerifyToastMessage = (
tableName,
columnDetails = true,
columnName = [],
columnDataType = [],
defaultValue,
columnDefaultValue = []
) => {
cy.get(databaseSelectors.addTableButton).click();
verifyAllElementsOfAddTableSection();
cy.clearAndType(databaseSelectors.tableNameInputField, tableName);
if (columnDetails) {
for (let i = 0; i < columnName.length; i++) {
cy.get(databaseSelectors.addMoreColumnsButton).click();
addNewColumnAndVerify(columnName[i], columnDataType[i], defaultValue, columnDefaultValue[i])
addNewColumnAndVerify(
columnName[i],
columnDataType[i],
defaultValue,
columnDefaultValue[i]
);
}
}
cy.get(commonSelectors.buttonSelector(commonText.createButton)).click();
@ -37,18 +65,29 @@ export const createTableAndVerifyToastMessage = (tableName, columnDetails = true
databaseText.tableCreatedSuccessfullyToast(tableName)
);
navigateToTable(tableName);
cy.get(databaseSelectors.idColumnHeader).verifyVisibleElement("have.text", databaseText.idColumnHeader);
cy.get(databaseSelectors.noRecordsText).verifyVisibleElement("have.text", databaseText.noRecordsText);
cy.get(databaseSelectors.idColumnHeader).verifyVisibleElement(
"have.text",
databaseText.idColumnHeader
);
cy.get(databaseSelectors.noRecordsText).verifyVisibleElement(
"have.text",
databaseText.noRecordsText
);
};
export const editTableNameAndVerifyToastMessage = (tableName, newTableName) => {
cy.get(databaseSelectors.currentTable(tableName))
.find(databaseSelectors.tableKebabIcon).invoke('show')
.trigger('mouseover')
.trigger('mousemove')
.trigger('mousedown')
.trigger('mouseup').click();
.find(databaseSelectors.tableKebabIcon)
.invoke("show")
.trigger("mouseover")
.trigger("mousemove")
.trigger("mousedown")
.trigger("mouseup")
.click();
cy.get(databaseSelectors.tableEditOption).click();
cy.get(databaseSelectors.editTableHeader).verifyVisibleElement("have.text", databaseText.editTableHeader);
cy.get(databaseSelectors.editTableHeader).verifyVisibleElement(
"have.text",
databaseText.editTableHeader
);
cy.get(databaseSelectors.tableNameLabel).verifyVisibleElement(
"have.text",
databaseText.tableNameLabel
@ -66,15 +105,20 @@ export const editTableNameAndVerifyToastMessage = (tableName, newTableName) => {
commonSelectors.toastMessage,
databaseText.tableEditedSuccessfullyToast(newTableName)
);
cy.get(databaseSelectors.currentTableName(newTableName)).verifyVisibleElement("have.text", newTableName);
cy.get(databaseSelectors.currentTableName(newTableName)).verifyVisibleElement(
"have.text",
newTableName
);
};
export const deleteTableAndVerifyToastMessage = (tableName) => {
cy.get(databaseSelectors.currentTable(tableName))
.find(databaseSelectors.tableKebabIcon).invoke('show')
.trigger('mouseover')
.trigger('mousemove')
.trigger('mousedown')
.trigger('mouseup').click();
.find(databaseSelectors.tableKebabIcon)
.invoke("show")
.trigger("mouseover")
.trigger("mousemove")
.trigger("mousedown")
.trigger("mouseup")
.click();
cy.get(databaseSelectors.tableDeleteOption).click();
// cy.on('window:confirm', (ConfirmAlertText) => {
// expect(ConfirmAlertText).to.contains(`Are you sure you want to delete the table "${tableName}"?`);
@ -84,62 +128,113 @@ export const deleteTableAndVerifyToastMessage = (tableName) => {
databaseText.tableDeletedSuccessfullyToast(tableName)
);
};
export const addNewColumnAndVerify = (columnName = [], columnDataType = [], defaultValue = true, columnDefaultValue = []) => {
cy.clearAndType(databaseSelectors.nameInputField("undefined"), columnName)
cy.get(databaseSelectors.nameInputField(columnName)).should("be.visible")
export const addNewColumnAndVerify = (
columnName = [],
columnDataType = [],
defaultValue = true,
columnDefaultValue = []
) => {
cy.clearAndType(databaseSelectors.nameInputField("undefined"), columnName);
cy.get(databaseSelectors.nameInputField(columnName))
.should("be.visible")
.verifyVisibleElement("have.value", columnName)
.parents(".list-group-item")
.within(() => {
cy.get(databaseSelectors.typeInputField).click();
cy.contains(`[id*="react-select-"]`, columnDataType).click();
if (defaultValue) {
cy.clearAndType(databaseSelectors.defaultInputField, columnDefaultValue)
cy.clearAndType(
databaseSelectors.defaultInputField,
columnDefaultValue
);
}
cy.get(databaseSelectors.typeInputField).should("be.visible")
.verifyVisibleElement("have.text", columnDataType)
cy.get(databaseSelectors.defaultInputField).should("be.visible")
.verifyVisibleElement("have.value", columnDefaultValue)
})
cy.get(databaseSelectors.typeInputField)
.should("be.visible")
.verifyVisibleElement("have.text", columnDataType);
cy.get(databaseSelectors.defaultInputField)
.should("be.visible")
.verifyVisibleElement("have.value", columnDefaultValue);
});
};
export const createNewColumnAndVerify = (tableName, columnName, columnDataType, defaultValue = true, columnDefaultValue) => {
navigateToTable(tableName)
cy.get(createNewColumnSelectors.addNewColumnButton).should('be.visible')
export const createNewColumnAndVerify = (
tableName,
columnName,
columnDataType,
defaultValue = true,
columnDefaultValue
) => {
navigateToTable(tableName);
cy.get(createNewColumnSelectors.addNewColumnButton)
.should("be.visible")
.click();
cy.get(createNewColumnSelectors.createNewColumnHeader).verifyVisibleElement("have.text", createNewColumnText.createNewColumnHeader)
cy.get(createNewColumnSelectors.columnNameLabel).verifyVisibleElement("have.text", createNewColumnText.columnNameLabel);
cy.get(createNewColumnSelectors.dataTypeLabel).verifyVisibleElement("have.text", createNewColumnText.dataTypeLabel);
cy.get(createNewColumnSelectors.defaultValueLabel).verifyVisibleElement("have.text", createNewColumnText.defaultValueLabel);
cy.get(createNewColumnSelectors.createNewColumnHeader).verifyVisibleElement(
"have.text",
createNewColumnText.createNewColumnHeader
);
cy.get(createNewColumnSelectors.columnNameLabel).verifyVisibleElement(
"have.text",
createNewColumnText.columnNameLabel
);
cy.get(createNewColumnSelectors.dataTypeLabel).verifyVisibleElement(
"have.text",
createNewColumnText.dataTypeLabel
);
cy.get(createNewColumnSelectors.defaultValueLabel).verifyVisibleElement(
"have.text",
createNewColumnText.defaultValueLabel
);
cy.clearAndType(createNewColumnSelectors.columnNameInputField, columnName);
cy.get(createNewColumnSelectors.dataTypeDropdown).click()
cy.get(createNewColumnSelectors.dataTypeDropdown).click();
cy.contains(`[id*="react-select-"]`, columnDataType).click();
if (defaultValue) {
cy.clearAndType(createNewColumnSelectors.defaultValueInputField, columnDefaultValue);
cy.get(createNewColumnSelectors.defaultValueInputField).should("be.visible")
cy.clearAndType(
createNewColumnSelectors.defaultValueInputField,
columnDefaultValue
);
cy.get(createNewColumnSelectors.defaultValueInputField)
.should("be.visible")
.verifyVisibleElement("have.value", columnDefaultValue);
}
cy.get(createNewColumnSelectors.columnNameInputField).should("be.visible")
cy.get(createNewColumnSelectors.columnNameInputField)
.should("be.visible")
.verifyVisibleElement("have.value", columnName);
cy.get(createNewColumnSelectors.dataTypeDropdown).should("be.visible")
cy.get(createNewColumnSelectors.dataTypeDropdown)
.should("be.visible")
.verifyVisibleElement("contain", columnDataType);
cy.get(commonSelectors.buttonSelector(commonText.cancelButton))
.should("be.visible")
.and("have.text", commonText.cancelButton);
cy.get(commonSelectors.buttonSelector(commonText.createButton))
.should("be.visible")
.and("have.text", commonText.createButton).click();
.and("have.text", commonText.createButton)
.click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
createNewColumnText.columnCreatedSuccessfullyToast
);
cy.get(databaseSelectors.columnHeader(columnName)).verifyVisibleElement("have.text", `${String(columnName).toLowerCase().replace(/\s+/g, "-")}`);
cy.get(databaseSelectors.columnHeader(columnName)).verifyVisibleElement(
"have.text",
`${String(columnName).toLowerCase().replace(/\s+/g, "-")}`
);
};
export const addNewRowAndVerify = (tableName, noDefaultValue = true, columnName = [], columnDefaultValue = []) => {
export const addNewRowAndVerify = (
tableName,
noDefaultValue = true,
columnName = [],
columnDefaultValue = []
) => {
navigateToTable(tableName);
cy.get(createNewRowSelectors.addNewRowButton).click();
cy.get(createNewRowSelectors.createNewRowHeader).verifyVisibleElement("have.text", createNewRowText.createNewRowHeader);
cy.get(createNewRowSelectors.idColumnNameLabel).verifyVisibleElement("contain", databaseText.idColumnHeader);
cy.get(createNewRowSelectors.createNewRowHeader).verifyVisibleElement(
"have.text",
createNewRowText.createNewRowHeader
);
cy.get(createNewRowSelectors.idColumnNameLabel).verifyVisibleElement(
"contain",
databaseText.idColumnHeader
);
cy.get(createNewRowSelectors.idColumnInputField).should("be.visible");
cy.get(commonSelectors.buttonSelector(commonText.cancelButton))
.should("be.visible")
@ -147,24 +242,32 @@ export const addNewRowAndVerify = (tableName, noDefaultValue = true, columnName
cy.get(commonSelectors.buttonSelector(commonText.createButton))
.should("be.visible")
.and("have.text", commonText.createButton);
cy.get('body').find(".table>>>th").its('length').then(columnLength => {
if (columnLength != 2) {
for (let i = 0; i < columnName.length; i++) {
if (noDefaultValue) {
cy.clearAndType(createNewRowSelectors.columnNameInputField(columnName[i]), columnDefaultValue[i])
}
else {
cy.get(createNewRowSelectors.columnNameInputField(columnName[i]))
.invoke('val')
.then(val => {
if (val === "") {
cy.clearAndType(createNewRowSelectors.columnNameInputField(columnName[i]), columnDefaultValue[i])
}
});
cy.get("body")
.find(".table>>>th")
.its("length")
.then((columnLength) => {
if (columnLength != 2) {
for (let i = 0; i < columnName.length; i++) {
if (noDefaultValue) {
cy.clearAndType(
createNewRowSelectors.columnNameInputField(columnName[i]),
columnDefaultValue[i]
);
} else {
cy.get(createNewRowSelectors.columnNameInputField(columnName[i]))
.invoke("val")
.then((val) => {
if (val === "") {
cy.clearAndType(
createNewRowSelectors.columnNameInputField(columnName[i]),
columnDefaultValue[i]
);
}
});
}
}
}
}
});
});
cy.get(commonSelectors.buttonSelector(commonText.createButton)).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
@ -172,7 +275,6 @@ export const addNewRowAndVerify = (tableName, noDefaultValue = true, columnName
);
cy.get('[data-cy*="-column-id-table-cell"]').should("be.visible");
};
export const verifyAllElementsOfAddTableSection = () => {
cy.get(databaseSelectors.tableNameLabel).verifyVisibleElement(
"have.text",
@ -208,7 +310,8 @@ export const verifyAllElementsOfAddTableSection = () => {
.and("have.text", commonText.createButton);
cy.get(databaseSelectors.addMoreColumnsButton).click();
cy.get(databaseSelectors.nameInputField("undefined")).should("be.visible")
cy.get(databaseSelectors.nameInputField("undefined"))
.should("be.visible")
.parents(".list-group-item")
.within(() => {
cy.get(databaseSelectors.typeInputField).should("be.visible");
@ -216,7 +319,6 @@ export const verifyAllElementsOfAddTableSection = () => {
cy.get(databaseSelectors.deleteIcon).should("be.visible").click();
});
};
export const filterOperation = (
tableName,
columnName = [],
@ -252,14 +354,17 @@ export const filterOperation = (
cy.get(filterSelectors.selectOperationField).last().click();
cy.contains(`[id*="react-select-"]`, operation[i]).click();
cy.get(filterSelectors.valueInputField).last().clear().type(value[i]);
cy.get(filterSelectors.selectColumnField).last().should("have.text", String(columnName[i]).toLowerCase())
cy.get(filterSelectors.valueInputField).last().should("have.text", value[i]);
cy.get(filterSelectors.selectColumnField)
.last()
.should("have.text", String(columnName[i]).toLowerCase());
cy.get(filterSelectors.valueInputField)
.last()
.should("have.text", value[i]);
cy.wait("@dbLoad");
}
cy.get('.table-responsive').click();
cy.get(".table-responsive").click();
cy.get(databaseSelectors.idColumnHeader).should("be.visible");
};
export const sortOperation = (tableName, columnName = [], order = []) => {
navigateToTable(tableName);
cy.get(sortSelectors.sortButton).should("be.visible").click();
@ -275,39 +380,154 @@ export const sortOperation = (tableName, columnName = [], order = []) => {
for (let i = 1; i < columnName.length; i++) {
cy.get(sortSelectors.addConditionLink).click();
cy.get(sortSelectors.selectColumnField).last().click();
cy.contains(`[id*="react-select-"]`, String(columnName[i]).toLowerCase()).click();
cy.contains(
`[id*="react-select-"]`,
String(columnName[i]).toLowerCase()
).click();
cy.get(sortSelectors.selectOrderField).last().click();
cy.contains(`[id*="react-select-"]`, order[0]).click();
}
cy.get(sortSelectors.sortButton).click();
cy.get(databaseSelectors.idColumnHeader).should("be.visible");
};
export const deleteCondition = (selector, columnName = [], deleteIcon) => {
cy.get(selector).click();
cy.get('.card-body').eq(1).should("be.visible");
cy.log(columnName.length)
cy.get(".card-body").eq(1).should("be.visible");
for (let i = 0; i < columnName.length; i++) {
cy.get(deleteIcon).eq(i).click();
}
};
export const deleteRowAndVerify = (tableName, rowNumber = []) => {
cy.get('body').find(".table>>tr").its('length').then(totalRowLength => {
cy.log(totalRowLength)
let rowsWithoutHeaderLength = totalRowLength - 1;
cy.log(rowsWithoutHeaderLength)
for (let i = 0; i < rowNumber.length; i++) {
cy.get(databaseSelectors.checkboxCell(rowNumber[i])).click();
}
cy.get(databaseSelectors.deleteRecordButton).should("be.visible").click();
navigateToTable(tableName);
cy.get("body")
.find(".table>>tr")
.its("length")
.then(() => {
for (let i = 0; i < rowNumber.length; i++) {
cy.get(databaseSelectors.checkboxCell(rowNumber[i])).click();
}
cy.get(databaseSelectors.deleteRecordButton).should("be.visible").click();
// cy.on('window:confirm', (ConfirmText) => {
// expect(ConfirmText).to.equal('Are you sure you want to delete the selected rows?');
// })
// cy.on('window:confirm', (ConfirmText) => {
// expect(ConfirmText).to.equal('Are you sure you want to delete the selected rows?');
// })
cy.verifyToastMessage(
commonSelectors.toastMessage,
databaseText.deleteRowToast(tableName, rowNumber.length)
);
});
};
export const verifyRowData = (rowNumber, columnName = [], rowData = []) => {
for (let i = 0; i < columnName.length - 1; i++) {
cy.get(editRowSelectors.getRowData(rowNumber, columnName[i + 1]))
.invoke("text")
.then((text) => {
expect(text).to.contain(rowData[i]);
});
}
};
export const editRowAndVerify = (
tableName,
rowNumber,
columnName = [],
rowFieldData = []
) => {
cy.reload();
cy.intercept("GET", "api/tooljet_db/organizations/**").as("dbLoad");
navigateToTable(tableName);
cy.wait("@dbLoad");
cy.get(editRowSelectors.editRowbutton).should("be.visible").click();
cy.get(editRowSelectors.editRowHeader).verifyVisibleElement(
"have.text",
editRowText.editRowHeader
);
cy.get(editRowSelectors.idColumnNameLabel).verifyVisibleElement(
"contain",
databaseText.idColumnName
);
cy.contains(createNewRowText.serialDataTypeLabel).should("be.visible");
cy.contains(editRowText.selectRowToEditText).should("be.visible");
cy.get(editRowSelectors.selectRowDropdown).should("be.visible").click();
cy.get(editRowSelectors.selectRowDropdown).type(rowNumber);
cy.get(`[id*="react-select-"]`).should("be.visible");
cy.contains(`[id*="react-select-"]`, rowNumber).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
databaseText.deleteRowToast(tableName, rowNumber.length)
);
})
}
for (let i = 0; i < columnName.length - 1; i++) {
cy.get(
createNewRowSelectors.columnNameLabel(columnName[i + 1])
).verifyVisibleElement("contain", columnName[i + 1].toLowerCase());
cy.get(
createNewRowSelectors.columnNameInputField(columnName[i + 1])
).should("be.visible");
cy.get(createNewRowSelectors.columnNameInputField(columnName[i + 1]))
.realClick()
.clear()
.realType(`${rowFieldData[i]}`);
cy.wait(500);
cy.get(createNewRowSelectors.columnNameInputField(columnName[i + 1]))
.invoke("val")
.then((text) => {
expect(text).to.contain(rowFieldData[i]);
});
}
cy.get(
commonSelectors.buttonSelector(commonText.cancelButton)
).verifyVisibleElement("have.text", commonText.cancelButton);
cy.get(commonSelectors.buttonSelector(commonText.saveChangesButton))
.verifyVisibleElement("have.text", commonText.saveChangesButton)
.realClick();
cy.verifyToastMessage(
commonSelectors.toastMessage,
createNewRowText.rowCreatedSuccessfullyToast
);
verifyRowData(rowNumber, columnName, rowFieldData);
};
export const editRowWithInvalidData = (
tableName,
rowNumber,
columnName,
rowFieldData
) => {
cy.intercept("GET", "api/tooljet_db/organizations/**").as("dbLoad");
navigateToTable(tableName);
cy.wait("@dbLoad");
cy.get(editRowSelectors.editRowbutton).should("be.visible").click();
cy.get(editRowSelectors.editRowHeader).verifyVisibleElement(
"have.text",
editRowText.editRowHeader
);
cy.contains(editRowText.selectRowToEditText).should("be.visible");
cy.get(editRowSelectors.selectRowDropdown).should("be.visible").click();
cy.get(editRowSelectors.selectRowDropdown).type(rowNumber);
cy.get(`[id*="react-select-"]`).should("be.visible");
cy.contains(`[id*="react-select-"]`, rowNumber).click();
cy.get(
createNewRowSelectors.columnNameLabel(columnName)
).verifyVisibleElement("contain", columnName.toLowerCase());
cy.get(createNewRowSelectors.columnNameInputField(columnName)).should(
"be.visible"
);
cy.get(createNewRowSelectors.columnNameInputField(columnName))
.realClick()
.clear()
.realType(`${rowFieldData}`);
cy.wait(500);
cy.get(createNewRowSelectors.columnNameInputField(columnName))
.invoke("val")
.then((text) => {
expect(text).to.contain(rowFieldData);
});
cy.get(
commonSelectors.buttonSelector(commonText.cancelButton)
).verifyVisibleElement("have.text", commonText.cancelButton);
cy.get(commonSelectors.buttonSelector(commonText.saveChangesButton))
.verifyVisibleElement("have.text", commonText.saveChangesButton)
.click();
cy.get(commonSelectors.buttonSelector(commonText.saveChangesButton)).should(
"be.disabled"
);
cy.get(commonSelectors.buttonSelector(commonText.cancelButton)).click();
};

View file

@ -105,6 +105,18 @@ export const verifyOnboardingQuestions = (fullName, workspaceName) => {
commonText.sizeOftheCompanyHeader
);
verifyandModifySizeOftheCompany();
cy.get(commonSelectors.backArrow).should("be.visible");
cy.get(commonSelectors.onboardingPageHeader).verifyVisibleElement(
"have.text",
"Enter your phone number"
);
cy.get(".form-control").should("be.visible");
cy.get(".tj-onboarding-phone-input-wrapper")
.find("input")
.type("919876543210");
cy.get(commonSelectors.continueButton).click();
};
export const verifyInvalidInvitationLink = () => {

View file

@ -15,7 +15,7 @@ This repository contains the ToolJet documentation website code and Markdown sou
- [Local setup](#local-setup)
## Feedback
If you want to give documentation feedback, please join our [Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) and drop us a message.
If you want to give documentation feedback, please join our [Slack Community](https://tooljet.com/slack) and drop us a message.
## Documentation Issues
To enter documentation bugs or submit any feature request for documentation, please create a new [GitHub issue](https://github.com/ToolJet/ToolJet/issues/new?assignees=&labels=documentation&template=03_documentation_report.yml&title=%5Bdocs%5D%3A+). Please check if there is an existing issue first.

View file

@ -23,7 +23,7 @@ The user details entered while setting up ToolJet will have Super Admin privileg
| [Manage any workspace's setting (Groups/SSO/Workspace Variables)](#manage-workspace-setting-groupsssoworkspace-variables) | ❌ | ✅ |
| [Manage all users from all the workspaces in the instance](#checking-all-the-users-in-the-instance) | ❌ | ✅ |
| [Make any user Super Admin](#make-the-user-super-admin) | ❌ | ✅ |
| [Restrict personal workspace of invited users](#allow-users-to-create-personal-workspace) | ❌ | ✅ |
| [Restrict creation of personal workspace of users](#restrict-creation-of-personal-workspace-of-users) | ❌ | ✅ |
<div style={{textAlign: 'center'}}>
@ -117,11 +117,11 @@ The user will become Super Admin and the Type column will update from **`workspa
</div>
### Allow users to create personal workspace
### Restrict creation of personal workspace of users
When a user joins a workspace, they are provided with their own personal workspace and option to create new workspaces.
Super Admins can control this behavior from the Manage Instance Settings page, they can **toggle off** the option to **Allow personal workspace**. Now whenever a user joins a workspace they won't be provided a personal workspace nor they will be able to create a new workspace in the instance.
Super Admins can **control** this behavior from the Manage Instance Settings page, they can **toggle off** the option to **Allow personal workspace**. Now whenever a user joins a workspace they won't be provided a personal workspace nor they will be able to create a new workspace in the instance.
<div style={{textAlign: 'center'}}>

View file

@ -179,4 +179,4 @@ docker-compose run --rm server npm --prefix server run test <path-to-file>
## Troubleshooting
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our Slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our [Slack Community](https://tooljet.com/slack) if you encounter any issues when trying to run ToolJet locally.

View file

@ -52,5 +52,5 @@ ToolJet allows you to internally utilize these libraries:
| Axios | [https://axios-http.com/docs/intro](https://axios-http.com/docs/intro) |
:::info
Issues with writing custom JavaScript code? Ask in our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg).
Issues with writing custom JavaScript code? Ask in our [Slack Community](https://tooljet.com/slack).
:::

View file

@ -0,0 +1,96 @@
---
id: import-external-libraries-using-runjs
title: Import external libraries using RunJS
---
ToolJet allows you to utilize external libraries in your app by importing them using the [RunJS query](/docs/data-sources/run-js).
In this how-to guide, we will import a few JavaScript libraries and use it in the application.
:::tip
You can import any of the available libraries using their **CDN**. Find free CDN of the open source projects at **[jsDelivr](https://www.jsdelivr.com/)**
:::
- Create a new application and then create a new RunPy query from the query panel.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/newquery.png" alt="Import external libraries using RunJS" />
</div>
- Let's write some code for importing libraries. We will first create a function `addScript` that returns a `Promise`, the `Promise` creates a script tag -> sets an attribute -> and eventListener `resolves` if its loaded and `rejects` if there is an error, and then body is appended at the end.
- We are going to import two libraries using their CDNs: **MathJS** and **Flatten**, and display an alert when the libraries are loaded successfully.
```js
function addScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.setAttribute('src', src);
s.addEventListener('load', resolve);
s.addEventListener('error', reject);
document.body.appendChild(s);
});
}
try {
await addScript('https://cdn.jsdelivr.net/npm/mathjs@11.7.0');
await addScript('https://cdn.jsdelivr.net/npm/flattenjs@2.1.3/lib/flatten.min.js');
await actions.showAlert("success", 'Mathjs and Flatten imported')
} catch (e) {
console.log(e);
}
```
- Now, when you hit **create** and then **run** the query, the script will be injected into the DOM. An alert should pop-up with the message **Mathjs and Flatten imported**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/imported.png" alt="Import external libraries using RunJS"/>
</div>
:::tip
Enable the **Run this query on application load?** option to make the libraries available throughout the application as soon as the app is laoded.
:::
## Examples
### Flatten the JSON objects using FlattenJS
- Let's create a new **RunJS** query that will use **Flatten** library(imported in the above section) and the query will flatten the JSON object.
```js
return flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/flatten.png" alt="Import external libraries using RunJS"/>
</div>
### Computation using MathJS
- Let's create a new **RunJS** query that will return the result of calculation performed by [atan2](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) method and then divided by [pi](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI).
```js
return math.atan2(3, -3) / math.pi
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/mathjs.png" alt="Import external libraries using RunJS"/>
</div>

View file

@ -185,9 +185,20 @@ actions.showAlert('error' , 'This is an error' )
</div>
## Run multiple actions from runjs query
To run multiple actions from a runjs query, you'll have to use **async-await** in the function.
Here is a example code snippet for running the queries and showing alert after specific intervals. Check the complete guide on running queries at specified intervals **[here](/docs/next/how-to/run-query-at-specified-intervals)**.
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```

View file

@ -28,6 +28,15 @@ In this how-to guide, we will learn how to make a query trigger at the specific
queries.post.run()
}
```
- Or use **async**-**await** in the function, if you're triggering multiple actions:
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```
- Go to the **Advanced** tab of the query, enable `Run query on page load?` this will trigger this RunJS query when the app is loaded. Name the query as `set` and **Save** it. Note that you will have to save the query and not `Save and Run` because doing it will trigger the query and you won't be able to stop the query unless you reload the page or go back to dashboard.
<div style={{textAlign: 'center'}}>

View file

@ -116,17 +116,13 @@ You can specify a different server for backend if it is hosted on another server
| ----------- | ------------------------------------------------------------------------------------------------- |
| SERVER_HOST | Configure a hostname for the server as a proxy pass. If no value is set, it defaults to `server`. |
#### Disable Multi-Workspace ( optional )
If you want to disable Multi-Workspace feature, set the environment variable `DISABLE_MULTI_WORKSPACE` to `true`.
### Hide account setup link
If you want to hide account setup link from admin in manage user page, set the environment variable `HIDE_ACCOUNT_SETUP_LINK` to `true`, please make sure you have configured SMTP to receive welcome mail for users. Valid only if `DISABLE_MULTI_WORKSPACE` is not `true`.
If you want to hide account setup link from admin in manage user page, set the environment variable `HIDE_ACCOUNT_SETUP_LINK` to `true`, please make sure you have configured SMTP to receive welcome mail for users.
#### Disabling signups ( optional )
Sign up is enabled only if Multi-Workspace is enabled. If you want to restrict the signups and allow new users only by invitations, set the environment variable `DISABLE_SIGNUPS` to `true`.
If you want to restrict the signups and allow new users only by invitations, set the environment variable `DISABLE_SIGNUPS` to `true`.
:::tip
You will still be able to see the signup page but won't be able to successfully submit the form.
@ -237,7 +233,7 @@ The maximum retry limit of login password for a user is by default set to 5, acc
#### SSO Configurations (Optional)
Configurations for instance level SSO. Valid only if `DISABLE_MULTI_WORKSPACE` is not `true`.
Configurations for instance level SSO.
| variable | description |
| ---------------------------- | -------------------------------------------------------------- |

View file

@ -9,4 +9,4 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocsCardList list={useCurrentSidebarCategory().items} />
```
If you have any questions feel free to join our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) or send us an email at hello@tooljet.com.
If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.

View file

@ -29,5 +29,5 @@ Server may take some time to be ready to handle the HTTP request as v2 changes r
- Docker compose deployments with [auto SSL](/docs/1.x.x/setup/docker) is deprecated
## Help and Support
- Feel free to join our highly active **[Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg)** or you can also e-mail us at **hello@tooljet.com**.
- Feel free to join our highly active **[Slack Community](https://tooljet.com/slack)** or you can also e-mail us at **hello@tooljet.com**.
- If you have found a bug, please create a **[GitHub issue](https://github.com/ToolJet/ToolJet/issues)** for the same.

View file

@ -298,5 +298,5 @@ This operation deletes a record from the table
- **Filter**: Add a condition by choosing a column, an operation, and the value for deleting a particular record.
:::info
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com)**
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com/slack)**
:::

View file

@ -0,0 +1,65 @@
---
id: workspace-variables
title: Workspace Variables
---
Workspace Variables are the variables with some value(usually tokens/secret keys/API keys) that can be used in different apps across the same Workspace.
## How can we add these variables to an Workspace?
Suppose there is an `API key` or a value that you want to use in the queries or widgets in the multiple apps of the same Workspace then the Workspace admin or the user with permissions can add an environment variable.
#### Adding the environment variable
- Go to the ToolJet Dashboard, and click on the dropdown on the navigation bar to show `Workspace` options
- Select `Manage Environment Variables`
- Click on `Add New Variable` button
- Give a `Name` to the variable, set the value, choose `Type`, toggle `Encryption`, and click **Add Variable** button
- Now this variable can be used inside any application of this Workspace
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/work-var.gif" alt="add variable" />
</div>
### Types of variables
- **Client**: The client variable can be used in widgets and queries.
- **Server**: The server variables can be used with all the queries except the `RunJS`. The reason why we don't allow the server variables to be used with the widgets is that these variables are only resolved during the runtime so they're highly secured.
:::info
Variable Type cannot be changed once it has been created.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/variable-type.png" alt="variable-type" width="700"/>
</div>
### Encryption
This feature enables us to add a client variable with and without `encryption`. The server variables are always encrypted by default.
### Using variable in an app
Let's use the variable that we created [here](Workspace-environment-variables#adding-the-environment-variable). If you have used ToolJet before, then you know that for getting the values from any variable we use JS notation i.e. `{{}}` but for using the Workspace variables we have different opening and closing notation `%% %%`. The environment variables will not work inside js code `{{}}`.
So, the syntax for using the variable that we created before will be `%%client.pi%%`
**Example for client variable usage:**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/variable-usage.png" alt="variable-usage" width="700"/>
</div>
**Example for server variable usage:**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/server-variable-usage.png" alt="server-variable-usage" width="700" />
</div>

View file

@ -0,0 +1,42 @@
---
id: workspace_overview
title: 'Workspace: Overview'
---
# Workspace: Overview
User can create their own workspaces, user who created workspace will be having admin privileges for the workspace.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/multiworkspace/multiwork.gif" alt="multi workspace" />
</div>
## Hierarchy
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/multiworkspace/Tooljet-workspace.png" alt="tooljet workspace" />
</div>
## Permissions
:::tip
Please check the detailed doc on **[Permissions](/docs/org-management/permissions)**.
:::
- The administrator can manage [users and groups](/docs/tutorial/manage-users-groups) of each workspace
- Applications and settings can not be shared between workspaces
- A user authorised to login to ToolJet will not have access to all workspaces, Users should be invited or signed up to a workspace to log-in to it.
- When Multi-Workspace feature is enabled, user should login with username and password to log in to Tooljet.
- Administrator can configure authentication methods for their workspaces.
- If password login is enabled, switching to the workspace will happen without any other authorization since the user is already authorized with password login.
- User logged in to Toojet and trying to switch to a workspace where SSO is enabled and password login is disabled, will be redirected to workspace login page and enabled SSO options will be shown
- User can directly login to a workspace using workspace login URL, Administrator can view the URL **Manage SSO -> General Settings -> Login URL**.
### When disabled (Super Admin)
- Only **[Super Admins](/docs//Enterprise/superadmin#restrict-creation-of-personal-workspace-of-users)** can disable the option for creating personal workspaces for a user.
- If creating personal workspaces is disabled, Create workspace feature wont be available.
- No separate login page for workspace and SSO configured for the workspace will be reflected to the main login page/login.

View file

@ -49,14 +49,14 @@ title: GitHub
Lastly, enter **Client Id** and **Client Secret** in GitHub manage SSO page and save.
The GitHub sign-in button will now be available in your ToolJet login screen if you have not enabled Multi-Workspace.
The GitHub sign-in button will now be available in your ToolJet login screen.
:::info
Should configure `Host Name` if you are using GitHub Enterprise self hosted. Host name should be a URL and should not ends with `/`, example: `https://github.tooljet.com`
:::
## Multi-Workspace
If you have enabled Multi-Workspace you can configure GitHub SSO as mentioned above, for setting default SSO for the instance use environment variable.
## Setting default SSO
To set GitHub as default SSO for the instance use environment variable.
| variable | description |
| ------------------------------------- | ----------------------------------------------------------- |
@ -64,4 +64,4 @@ If you have enabled Multi-Workspace you can configure GitHub SSO as mentioned ab
| SSO_GIT_OAUTH2_CLIENT_SECRET | GitHub OAuth client secret |
| SSO_GIT_OAUTH2_HOST | GitHub OAuth host name if GitHub is self hosted |
Redirect URL should be `<host>/sso/git`
**Redirect URL should be `<host>/sso/git`**

View file

@ -73,13 +73,13 @@ user who is signing in
Lastly, set the `client id` in google manage SSO page. This value will be available from your [Google cloud console credentials page](https://console.cloud.google.com/apis/credentials)
The Google sign-in button will now be available in your ToolJet login screen, if you are not enabled Multi-Workspace.
The Google sign-in button will now be available in your ToolJet login screen.
## Multi-Workspace
If you have enabled Multi-Workspace you can configure Google SSO as mentioned above, for setting default SSO for the instance use environment variable.
## Setting default SSO
To set Google as default SSO for the instance use environment variable.
| variable | description |
| ------------------------------------- | ----------------------------------------------------------- |
| SSO_GOOGLE_OAUTH2_CLIENT_ID | Google OAuth client id |
Redirect URL should be `<host>/sso/google`
**Redirect URL should be `<host>/sso/google`**

View file

@ -5,75 +5,8 @@ title: User Lifecycle
# User Lifecycle
## Single-Workspace
### User onboarding
- If no user is present in the system, there will be `Sign-up` option in the login page. User can sign up by entering their email address. Tooljet will be sending a welcome email with activation URL to the email address. User can follow the activation URL and onboard to ToolJet.
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace sign up](/img/user-lifecycle/single-ws-signup.png)
</div>
- User with admin privileges can invite members
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace invite user](/img/user-lifecycle/user-invite-sw.png)
</div>
- Invited user will receive welcome email with activation URL, unregistered user can follow the link and setup Tooljet account
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace accept invite](/img/user-lifecycle/accept-invite-sw.png)
</div>
- Invited user can onboard through SSO login, without using an invitation link
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace accept invite](/img/user-lifecycle/sso-onboard-sw.png)
</div>
- If `enable signup` option in enabled in SSO general settings, user can setup account through SSO login without an invite
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace sign up using SSO](/img/user-lifecycle/sso-enable-signup-sw.png)
</div>
### Archive user
- User can be archived by workspace admin from using `Manage User` page
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace Archive user](/img/user-lifecycle/archive-user.png)
</div>
### Unarchive user
- User can be unArchived by workspace admin from using `Manage User` page
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace Unarchive user](/img/user-lifecycle/unarchive-sw.png)
</div>
:::info
Archive or unarchive will affect user login, user won't be able to login using email id and password unless user is in active state
:::
## Multi-Workspace
:::info
Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
Check Workspace docs [here](/docs/tutorial/workspace_overview).
:::
### User onboarding
@ -82,7 +15,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Multi-Workspace sign up](/img/user-lifecycle/signup-mw.png)
![ToolJet - Workspace sign up](/img/user-lifecycle/signup-mw.png)
</div>
@ -90,7 +23,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Multi-Workspace sign up](/img/user-lifecycle/user-mw.png)
![ToolJet - Workspace sign up](/img/user-lifecycle/user-mw.png)
</div>
@ -98,7 +31,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Multi-Workspace sign up](/img/user-lifecycle/invite-link-mw.png)
![ToolJet - Workspace sign up](/img/user-lifecycle/invite-link-mw.png)
</div>
@ -106,7 +39,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace accept invite](/img/user-lifecycle/sso-onboard-sw.png)
![ToolJet - Workspace accept invite](/img/user-lifecycle/sso-onboard-sw.png)
</div>
@ -114,7 +47,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace sign up using SSO](/img/user-lifecycle/sso-enable-signup-sw.png)
![ToolJet - Workspace sign up using SSO](/img/user-lifecycle/sso-enable-signup-sw.png)
</div>
@ -123,7 +56,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace Archive user](/img/user-lifecycle/archive-user.png)
![ToolJet -Workspace Archive user](/img/user-lifecycle/archive-user.png)
</div>
@ -144,7 +77,7 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
<div style={{textAlign: 'center'}}>
![ToolJet - Single-Workspace sign up using SSO](/img/user-lifecycle/switch.png)
![ToolJet - Workspace sign up using SSO](/img/user-lifecycle/switch.png)
</div>
@ -157,11 +90,11 @@ Check Multi-workspace docs [here](/docs/tutorial/multiworkspace).
| invited | No (Yes with SSO)| Login through SSO or invitation link |
| archived | No | Not able to activate. Invite from `Manage Users` page, status will be changed to invited |
## Multi-Workspace SSO flow
## Workspace SSO flow
- Diagram shows how SSO configurations are chosen in common login page and workspace login page. Instance level SSO is configured in environment variables and Workspace level SSO is configured in respective `Manage SSO` page.
<div style={{textAlign: 'center'}}>
![ToolJet - SSO Flow](/img/user-lifecycle/sso-flow.png)
![ToolJet - SSO Flow](/img/user-lifecycle/sso-flow.png)
</div>

View file

@ -109,6 +109,32 @@ Along with `changeSet`, `dataUpdates` property will also be changed when the val
If the data of a cell is changed, "save changes" button will be shown at the bottom of the table. This button when clicked will trigger the `Bulk update query` event. This event can be used to run a query to update the data on your data source.
### Use dynamic column
Enabling the **Use dynamic column** toggle will allow users to set the **Column data** where users can link the column data dynamically from a query.
The **column data** field expects a JSON value:
```json
{
"name":"Name",
"columnType":"string",
"key":"first_name",
"cellBackgroundColor":"#000",
"textColor":"#fff",
"isEditable":true,
"regex":"",
"maxLength":10,
"minLength":5,
"customRule":""
}
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/widgets/table/dynamic_column.png" alt="ToolJet - Widget Reference - Table" />
</div>
## Validation
Under column properties, expand the detailed view of a column type to access a toggle button called `make editable`. You can toggle it `ON` to apply the validations for each column respectively using the following.

View file

@ -56,7 +56,7 @@ module.exports = {
'aria-label': 'GitHub repository',
},
{
href: 'https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg',
href: 'https://tooljet.com/slack',
position: 'right',
className: 'navbar-social-link navbar-slack-logo',
'aria-label': 'Slack workspace',
@ -86,7 +86,7 @@ module.exports = {
items: [
{
label: 'Slack',
href: 'https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg',
href: 'https://tooljet.com/slack',
},
],
},
@ -127,7 +127,7 @@ module.exports = {
// Please change this to your repo.
editUrl: 'https://github.com/ToolJet/Tooljet/blob/develop/docs/',
includeCurrentVersion: false,
lastVersion: '2.2.0',
lastVersion: '2.4.0',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),

View file

@ -261,8 +261,8 @@ const sidebars = {
'type': 'category',
'label': 'Workspaces',
'items': [
'tutorial/multiworkspace',
'tutorial/workspace-environment-variables',
'tutorial/workspace_overview',
'tutorial/workspace-variables',
],
},
'org-management/permissions',
@ -301,6 +301,7 @@ const sidebars = {
'how-to/access-currentuser',
'how-to/use-axios-in-runjs',
'how-to/import-external-libraries-using-runpy',
'how-to/import-external-libraries-using-runjs',
'how-to/run-actions-from-runjs',
'how-to/run-query-at-specified-intervals',
'how-to/access-users-location',

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

View file

@ -190,4 +190,4 @@ docker-compose run --rm server npm --prefix server run test <path-to-file>
## Troubleshooting
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our Slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our [Slack Community](https://tooljet.com/slack) if you encounter any issues when trying to run ToolJet locally.

View file

@ -48,6 +48,6 @@ ToolJet allows you to internally utilize these libraries:
:::danger
Issues with writing custom JavaScript code? Ask in our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg).
Issues with writing custom JavaScript code? Ask in our [Slack Community](https://tooljet.com/slack).
:::

View file

@ -182,8 +182,20 @@ actions.showAlert(alert type , message ) // alert types are info, success, warni
</div>
## Run multiple actions from runjs query
To run multiple actions from a runjs query, you'll have to use async-await in the function.
Here is an sample code for running the queries and showing alert after specific intervals. Check the complete guide on running queries at specified intervals **[here](/docs/next/how-to/run-query-at-specified-intervals)**.
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```

View file

@ -28,6 +28,16 @@ In this how-to guide, we will learn how to make a query trigger at the specific
queries.post.run()
}
```
- Or use **async**-**await** in the function, if you're triggering multiple actions:
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```
- Go to the **Advanced** tab of the query, enable `Run query on page load?` this will trigger this RunJS query when the app is loaded. Name the query as `set` and **Save** it. Note that you will have to save the query and not `Save and Run` because doing it will trigger the query and you won't be able to stop the query unless you reload the page or go back to dashboard.
<div style={{textAlign: 'center'}}>

View file

@ -50,4 +50,4 @@ The references for data sources and widgets:
- We have extensively documented the features of ToolJet, but in case you are stuck, please feel free to e-mail us at **hello@tooljet.com**
- If you are using ToolJet cloud, click on the chat icon at the bottom-left corner for instant help.
- If you have found a bug, please create a **[GitHub issue](https://github.com/ToolJet/ToolJet/issues)** for the same.
- Feel free to join our highly active **[Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg)**.
- Feel free to join our highly active **[Slack Community](https://tooljet.com/slack)**.

View file

@ -9,4 +9,4 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocCardList items={useCurrentSidebarCategory().items}/>
```
If you have any questions feel free to join our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) or send us an email at hello@tooljet.com.
If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.

View file

@ -179,4 +179,4 @@ docker-compose run --rm server npm --prefix server run test <path-to-file>
## Troubleshooting
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our Slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
SlackPlease open a new issue at https://github.com/ToolJet/ToolJet/issues or join our [Slack Community](https://tooljet.com/slack) if you encounter any issues when trying to run ToolJet locally.

View file

@ -52,5 +52,5 @@ ToolJet allows you to internally utilize these libraries:
| Axios | [https://axios-http.com/docs/intro](https://axios-http.com/docs/intro) |
:::info
Issues with writing custom JavaScript code? Ask in our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg).
Issues with writing custom JavaScript code? Ask in our [Slack Community](https://tooljet.com/slack).
:::

View file

@ -0,0 +1,96 @@
---
id: import-external-libraries-using-runjs
title: Import external libraries using RunJS
---
ToolJet allows you to utilize external libraries in your app by importing them using the [RunJS query](/docs/data-sources/run-js).
In this how-to guide, we will import a few JavaScript libraries and use it in the application.
:::tip
You can import any of the available libraries using their **CDN**. Find free CDN of the open source projects at **[jsDelivr](https://www.jsdelivr.com/)**
:::
- Create a new application and then create a new RunPy query from the query panel.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/newquery.png" alt="Import external libraries using RunJS" />
</div>
- Let's write some code for importing libraries. We will first create a function `addScript` that returns a `Promise`, the `Promise` creates a script tag -> sets an attribute -> and eventListener `resolves` if its loaded and `rejects` if there is an error, and then body is appended at the end.
- We are going to import two libraries using their CDNs: **MathJS** and **Flatten**, and display an alert when the libraries are loaded successfully.
```js
function addScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.setAttribute('src', src);
s.addEventListener('load', resolve);
s.addEventListener('error', reject);
document.body.appendChild(s);
});
}
try {
await addScript('https://cdn.jsdelivr.net/npm/mathjs@11.7.0');
await addScript('https://cdn.jsdelivr.net/npm/flattenjs@2.1.3/lib/flatten.min.js');
await actions.showAlert("success", 'Mathjs and Flatten imported')
} catch (e) {
console.log(e);
}
```
- Now, when you hit **create** and then **run** the query, the script will be injected into the DOM. An alert should pop-up with the message **Mathjs and Flatten imported**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/imported.png" alt="Import external libraries using RunJS"/>
</div>
:::tip
Enable the **Run this query on application load?** option to make the libraries available throughout the application as soon as the app is laoded.
:::
## Examples
### Flatten the JSON objects using FlattenJS
- Let's create a new **RunJS** query that will use **Flatten** library(imported in the above section) and the query will flatten the JSON object.
```js
return flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/flatten.png" alt="Import external libraries using RunJS"/>
</div>
### Computation using MathJS
- Let's create a new **RunJS** query that will return the result of calculation performed by [atan2](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) method and then divided by [pi](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI).
```js
return math.atan2(3, -3) / math.pi
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/mathjs.png" alt="Import external libraries using RunJS"/>
</div>

View file

@ -182,7 +182,20 @@ actions.showAlert(alert type , message ) // alert types are info, success, warni
</div>
## Run multiple actions from runjs query
To run multiple actions from a runjs query, you'll have to use async-await in the function.
Here is an sample code for running the queries and showing alert after specific intervals. Check the complete guide on running queries at specified intervals **[here](/docs/next/how-to/run-query-at-specified-intervals)**.
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```

View file

@ -28,6 +28,16 @@ In this how-to guide, we will learn how to make a query trigger at the specific
queries.post.run()
}
```
- Or use **async**-**await** in the function, if you're triggering multiple actions:
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```
- Go to the **Advanced** tab of the query, enable `Run query on page load?` this will trigger this RunJS query when the app is loaded. Name the query as `set` and **Save** it. Note that you will have to save the query and not `Save and Run` because doing it will trigger the query and you won't be able to stop the query unless you reload the page or go back to dashboard.
<div style={{textAlign: 'center'}}>

View file

@ -24,7 +24,7 @@ There are two options to deploy ToolJet using Docker Compose:
1. **Using an external PostgreSQL database**. This setup is recommended if you want to use a managed PostgreSQL service such as AWS RDS or Google Cloud SQL.
2. **Using in-built PostgreSQL database**. This setup uses the official Docker image of PostgreSQL.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack
:::info
We recommend using the managed PostgreSQL service on production for ease of administration, security, and management (backups, monitoring, etc).

View file

@ -9,4 +9,4 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocsCardList list={useCurrentSidebarCategory().items} />
```
If you have any questions feel free to join our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) or send us an email at hello@tooljet.com.
If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.

View file

@ -29,5 +29,5 @@ Server may take some time to be ready to handle the HTTP request as v2 changes r
- Docker compose deployments with [auto SSL](/docs/1.x.x/setup/docker) is deprecated
## Help and Support
- Feel free to join our highly active **[Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg)** or you can also e-mail us at **hello@tooljet.com**.
- Feel free to join our highly active **[Slack Community](https://tooljet.com/slack)** or you can also e-mail us at **hello@tooljet.com**.
- If you have found a bug, please create a **[GitHub issue](https://github.com/ToolJet/ToolJet/issues)** for the same.

View file

@ -288,5 +288,5 @@ This operation deletes a record from the table
- **Filter**: Add a condition by choosing a column, an operation, and the value for deleting a particular record.
:::info
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com)**
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com/slack)**
:::

View file

@ -179,4 +179,4 @@ docker-compose run --rm server npm --prefix server run test <path-to-file>
## Troubleshooting
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our Slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our [Slack Community](https://tooljet.com/slack) if you encounter any issues when trying to run ToolJet locally.

View file

@ -52,5 +52,5 @@ ToolJet allows you to internally utilize these libraries:
| Axios | [https://axios-http.com/docs/intro](https://axios-http.com/docs/intro) |
:::info
Issues with writing custom JavaScript code? Ask in our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg).
Issues with writing custom JavaScript code? Ask in our [Slack Community](https://tooljet.com/slack).
:::

View file

@ -0,0 +1,96 @@
---
id: import-external-libraries-using-runjs
title: Import external libraries using RunJS
---
ToolJet allows you to utilize external libraries in your app by importing them using the [RunJS query](/docs/data-sources/run-js).
In this how-to guide, we will import a few JavaScript libraries and use it in the application.
:::tip
You can import any of the available libraries using their **CDN**. Find free CDN of the open source projects at **[jsDelivr](https://www.jsdelivr.com/)**
:::
- Create a new application and then create a new RunPy query from the query panel.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/newquery.png" alt="Import external libraries using RunJS" />
</div>
- Let's write some code for importing libraries. We will first create a function `addScript` that returns a `Promise`, the `Promise` creates a script tag -> sets an attribute -> and eventListener `resolves` if its loaded and `rejects` if there is an error, and then body is appended at the end.
- We are going to import two libraries using their CDNs: **MathJS** and **Flatten**, and display an alert when the libraries are loaded successfully.
```js
function addScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.setAttribute('src', src);
s.addEventListener('load', resolve);
s.addEventListener('error', reject);
document.body.appendChild(s);
});
}
try {
await addScript('https://cdn.jsdelivr.net/npm/mathjs@11.7.0');
await addScript('https://cdn.jsdelivr.net/npm/flattenjs@2.1.3/lib/flatten.min.js');
await actions.showAlert("success", 'Mathjs and Flatten imported')
} catch (e) {
console.log(e);
}
```
- Now, when you hit **create** and then **run** the query, the script will be injected into the DOM. An alert should pop-up with the message **Mathjs and Flatten imported**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/imported.png" alt="Import external libraries using RunJS"/>
</div>
:::tip
Enable the **Run this query on application load?** option to make the libraries available throughout the application as soon as the app is laoded.
:::
## Examples
### Flatten the JSON objects using FlattenJS
- Let's create a new **RunJS** query that will use **Flatten** library(imported in the above section) and the query will flatten the JSON object.
```js
return flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/flatten.png" alt="Import external libraries using RunJS"/>
</div>
### Computation using MathJS
- Let's create a new **RunJS** query that will return the result of calculation performed by [atan2](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) method and then divided by [pi](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI).
```js
return math.atan2(3, -3) / math.pi
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/mathjs.png" alt="Import external libraries using RunJS"/>
</div>

View file

@ -182,7 +182,20 @@ actions.showAlert(alert type , message ) // alert types are info, success, warni
</div>
## Run multiple actions from runjs query
To run multiple actions from a runjs query, you'll have to use async-await in the function.
Here is an sample code for running the queries and showing alert after specific intervals. Check the complete guide on running queries at specified intervals **[here](/docs/next/how-to/run-query-at-specified-intervals)**.
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```

View file

@ -28,6 +28,16 @@ In this how-to guide, we will learn how to make a query trigger at the specific
queries.post.run()
}
```
- Or use **async**-**await** in the function, if you're triggering multiple actions:
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```
- Go to the **Advanced** tab of the query, enable `Run query on page load?` this will trigger this RunJS query when the app is loaded. Name the query as `set` and **Save** it. Note that you will have to save the query and not `Save and Run` because doing it will trigger the query and you won't be able to stop the query unless you reload the page or go back to dashboard.
<div style={{textAlign: 'center'}}>

View file

@ -24,7 +24,7 @@ There are two options to deploy ToolJet using Docker Compose:
1. **Using an external PostgreSQL database**. This setup is recommended if you want to use a managed PostgreSQL service such as AWS RDS or Google Cloud SQL.
2. **Using in-built PostgreSQL database**. This setup uses the official Docker image of PostgreSQL.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack
:::info
We recommend using the managed PostgreSQL service on production for ease of administration, security, and management (backups, monitoring, etc).

View file

@ -9,4 +9,4 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocsCardList list={useCurrentSidebarCategory().items} />
```
If you have any questions feel free to join our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) or send us an email at hello@tooljet.com.
If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.

View file

@ -29,5 +29,5 @@ Server may take some time to be ready to handle the HTTP request as v2 changes r
- Docker compose deployments with [auto SSL](/docs/1.x.x/setup/docker) is deprecated
## Help and Support
- Feel free to join our highly active **[Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg)** or you can also e-mail us at **hello@tooljet.com**.
- Feel free to join our highly active **[Slack Community](https://tooljet.com/slack)** or you can also e-mail us at **hello@tooljet.com**.
- If you have found a bug, please create a **[GitHub issue](https://github.com/ToolJet/ToolJet/issues)** for the same.

View file

@ -288,5 +288,5 @@ This operation deletes a record from the table
- **Filter**: Add a condition by choosing a column, an operation, and the value for deleting a particular record.
:::info
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com)**
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com/slack)**
:::

View file

@ -179,4 +179,4 @@ docker-compose run --rm server npm --prefix server run test <path-to-file>
## Troubleshooting
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our Slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our [Slack Community](https://tooljet.com/slack) if you encounter any issues when trying to run ToolJet locally.

View file

@ -52,5 +52,5 @@ ToolJet allows you to internally utilize these libraries:
| Axios | [https://axios-http.com/docs/intro](https://axios-http.com/docs/intro) |
:::info
Issues with writing custom JavaScript code? Ask in our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg).
Issues with writing custom JavaScript code? Ask in our [Slack Community](https://tooljet.com/slack).
:::

View file

@ -0,0 +1,96 @@
---
id: import-external-libraries-using-runjs
title: Import external libraries using RunJS
---
ToolJet allows you to utilize external libraries in your app by importing them using the [RunJS query](/docs/data-sources/run-js).
In this how-to guide, we will import a few JavaScript libraries and use it in the application.
:::tip
You can import any of the available libraries using their **CDN**. Find free CDN of the open source projects at **[jsDelivr](https://www.jsdelivr.com/)**
:::
- Create a new application and then create a new RunPy query from the query panel.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/newquery.png" alt="Import external libraries using RunJS" />
</div>
- Let's write some code for importing libraries. We will first create a function `addScript` that returns a `Promise`, the `Promise` creates a script tag -> sets an attribute -> and eventListener `resolves` if its loaded and `rejects` if there is an error, and then body is appended at the end.
- We are going to import two libraries using their CDNs: **MathJS** and **Flatten**, and display an alert when the libraries are loaded successfully.
```js
function addScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.setAttribute('src', src);
s.addEventListener('load', resolve);
s.addEventListener('error', reject);
document.body.appendChild(s);
});
}
try {
await addScript('https://cdn.jsdelivr.net/npm/mathjs@11.7.0');
await addScript('https://cdn.jsdelivr.net/npm/flattenjs@2.1.3/lib/flatten.min.js');
await actions.showAlert("success", 'Mathjs and Flatten imported')
} catch (e) {
console.log(e);
}
```
- Now, when you hit **create** and then **run** the query, the script will be injected into the DOM. An alert should pop-up with the message **Mathjs and Flatten imported**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/imported.png" alt="Import external libraries using RunJS"/>
</div>
:::tip
Enable the **Run this query on application load?** option to make the libraries available throughout the application as soon as the app is laoded.
:::
## Examples
### Flatten the JSON objects using FlattenJS
- Let's create a new **RunJS** query that will use **Flatten** library(imported in the above section) and the query will flatten the JSON object.
```js
return flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/flatten.png" alt="Import external libraries using RunJS"/>
</div>
### Computation using MathJS
- Let's create a new **RunJS** query that will return the result of calculation performed by [atan2](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) method and then divided by [pi](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/PI).
```js
return math.atan2(3, -3) / math.pi
```
- Save the query, you can either **Preview** the output on the query manager or **Run** the query to check the output on the inspector on the left-sidebar.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/mathjs.png" alt="Import external libraries using RunJS"/>
</div>

View file

@ -182,7 +182,20 @@ actions.showAlert(alert type , message ) // alert types are info, success, warni
</div>
## Run multiple actions from runjs query
To run multiple actions from a runjs query, you'll have to use async-await in the function.
Here is an sample code for running the queries and showing alert after specific intervals. Check the complete guide on running queries at specified intervals **[here](/docs/next/how-to/run-query-at-specified-intervals)**.
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```

View file

@ -28,6 +28,16 @@ In this how-to guide, we will learn how to make a query trigger at the specific
queries.post.run()
}
```
- Or use **async**-**await** in the function, if you're triggering multiple actions:
```js
actions.setVariable('interval',setInterval(countdown, 5000));
async function countdown(){
await queries.restapi1.run()
await queries.restapi2.run()
await actions.showAlert('info','This is an information')
}
```
- Go to the **Advanced** tab of the query, enable `Run query on page load?` this will trigger this RunJS query when the app is loaded. Name the query as `set` and **Save** it. Note that you will have to save the query and not `Save and Run` because doing it will trigger the query and you won't be able to stop the query unless you reload the page or go back to dashboard.
<div style={{textAlign: 'center'}}>

View file

@ -24,7 +24,7 @@ There are two options to deploy ToolJet using Docker Compose:
1. **Using an external PostgreSQL database**. This setup is recommended if you want to use a managed PostgreSQL service such as AWS RDS or Google Cloud SQL.
2. **Using in-built PostgreSQL database**. This setup uses the official Docker image of PostgreSQL.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack
:::info
We recommend using the managed PostgreSQL service on production for ease of administration, security, and management (backups, monitoring, etc).

View file

@ -9,4 +9,4 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocsCardList list={useCurrentSidebarCategory().items} />
```
If you have any questions feel free to join our [Slack community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) or send us an email at hello@tooljet.com.
If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.

View file

@ -29,5 +29,5 @@ Server may take some time to be ready to handle the HTTP request as v2 changes r
- Docker compose deployments with [auto SSL](/docs/1.x.x/setup/docker) is deprecated
## Help and Support
- Feel free to join our highly active **[Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg)** or you can also e-mail us at **hello@tooljet.com**.
- Feel free to join our highly active **[Slack Community](https://tooljet.com/slack)** or you can also e-mail us at **hello@tooljet.com**.
- If you have found a bug, please create a **[GitHub issue](https://github.com/ToolJet/ToolJet/issues)** for the same.

View file

@ -298,5 +298,5 @@ This operation deletes a record from the table
- **Filter**: Add a condition by choosing a column, an operation, and the value for deleting a particular record.
:::info
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com)**
If you have any other questions or feedback about **ToolJet Database**, please reach us out at hello@tooljet.com or join our **[Slack Community](https://www.tooljet.com/slack)**
:::

View file

@ -0,0 +1,5 @@
{
"label": "Enterprise",
"position": 9,
"collapsed": true
}

View file

@ -0,0 +1,84 @@
---
id: audit_logs
title: Audit logs
---
<div className='badge badge--primary heading-badge'>Available on: Enterprise Edition</div>
The audit log is the report of all the activities done in your ToolJet account. It will capture and display events automatically by recording who performed an activity, what when, and where the activity was performed, along with other information such as IP address.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/audit_logs/auditlogv2.png" alt="Audit logs" />
</div>
### Filter audit logs
Audited events can be filtered using the below characteristics:
#### Select Users
Select a specific user from this dropdown to check all their activities.
#### Select Apps
The dropdown will list all the apps present in your account. Choose an app to filter the logs associated with that app.
#### Select Resources
| Resources | description |
| ----------- | ----------- |
| User | Filter all the User events like `USER_LOGIN`, `USER_SIGNUP`, `USER_INVITE`, AND `USER_INVITE_REDEEM`. |
| App | Filter all the App events like `APP_CREATE`, `APP_UPDATE`,`APP_VIEW`,`APP_DELETE`,`APP_IMPORT`,`APP_EXPORT`,`APP_CLONE`. |
| Data Query | Filters the events associated with Data Query like `DATA_QUERY_RUN`. |
| Group Permission | All the events associated with Group Permissions will be filtered. Group Permissions include `GROUP_CREATE`, `GROUP_UPDATE`, `GROUP_DELETE`. |
| App Group Permission | Within each group, you can set apps for read or edit privileges. These events get recorded as App Group Permissions. |
#### Select Actions
| Actions | description |
| ----------- | ----------- |
| USER_LOGIN | This event is recorded everytime a user logins. |
| USER_SIGNUP | This event is recorded everytime a new signup is made. |
| USER_INVITE | You can invite users to your account from `Manage Users` section and an event is audited everytime an invite is sent. |
| USER_INVITE_REDEEM | This event is recorded whenever an invite is redeemed. |
| APP_CREATE | This event is recorded when a user creates a new app. |
| APP_UPDATE | This event is recorded whenever actions like renaming the app, making the app public, editing shareable link, or deploying the app are made. |
| APP_VIEW | This event is logged when someone views the launched app. (public apps aren't accounted for) |
| APP_DELETE | This event is recorded whenever a user deletes an app from the dashboard. |
| APP_IMPORT | This event is recorded whenever a user imports an app. |
| APP_EXPORT | This event is recorded whenever an app is exported. |
| APP_CLONE | This event is recorded whenever a clone of the existing app is created. |
| DATA_QUERY_RUN | This event is logged whenever a data source is added, a query is created, or whenever a query is run either from the query editor or from the launched app. |
| GROUP_PERMISSION_CREATE | This event is recorded whenever a group is created. |
| GROUP_PERMISSION_UPDATE | This event is recorded whenever an app or user is added to or removed from a group, or the permissions for a group are updated. |
| GROUP_PERMISSION_DELETE | This event is recorded whenever a user group is deleted from an account. |
| APP_GROUP_PERMISSION_UPDATE | For every app added in to user group, you can set privileges like `View` or `Edit` and whenever these privileges are updated this event is recorded. By default, the permission of an app for a user group is set to `View`. |
:::info
It is mandatory to set a Data Range in `From` and `To` to filter audit logs.
:::
### Understanding information from logs
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/audit_logs/readinglogv2.png" alt="Audit logs" />
</div>
| Property | description |
| ----------- | ----------- |
| action_type | It is the type of action that was logged in this event. Refer [this](#select-actions) to know about actions. |
| created_at | Displays the date and time of a logged event. |
| id | Every event logged has a specific event id associated with it. |
| ip_address | Displays the IP address from where the event was logged. |
| metadata | Metadata includes two sub-properties - `tooljet_version` and `user_agent`. `tooljet_version` displays the version of ToolJet used for the logged event and `user_agent` contains information about the device and browser used for that event. |
| organization_id | Every organization in ToolJet has an id associated with it and is recorded when an event occurs. |
| resource_id | There are several [resources](#select-resources) and for each resource that is created, an id gets associated with it.|
| resource_name | Displays the name of the [resources](#select-resources) that were logged in the event. For example, if an app was created or deleted then it will display the name of the app. |
| resource_type | Displays the type of the [resources](#select-resources) that were logged in the event. |
| user_id | Every user account in ToolJet has an id associated with it and is recorded when an event occurs. |

View file

@ -0,0 +1,130 @@
---
id: superadmin
title: Super Admin
---
<div className='badge badge--primary heading-badge'>Available on: Enterprise Edition</div>
A Super Admin is the user who has full access to all the Workspaces, Users, and Groups of an instance. An instance can have more than one Super Admin. A Super Admin has full control over other users' workspaces and can create users, groups, and other super admins.
The user details entered while setting up ToolJet will have Super Admin privileges.
## How is Super Admin different from Admin
| Privilege | Admin | Super Admin |
| --------- | ----- | ----------- |
| Manage Users in their workspace (Invite/Archive/Unarchive) | ✅ | ✅ |
| Manage Groups in their workspace (Create Group/Add or Delete Users from groups/ Modify Group Permissions) | ✅ | ✅ |
| Manage SSO in their workspace | ✅ | ✅ |
| Manage Workspace Variables in their workspace | ✅ | ✅ |
| [Access any user's personal workspace (create, edit or delete apps)](#access-any-workspace) | ❌ | ✅ |
| [Archive Admin or any user of any workspace](#archiveunarchive-users) | ❌ | ✅ |
| [Access any user's ToolJet database (create, edit or delete database)](#access-tooljet-db-in-any-workspace) | ❌ | ✅ |
| [Manage any workspace's setting (Groups/SSO/Workspace Variables)](#manage-workspace-setting-groupsssoworkspace-variables) | ❌ | ✅ |
| [Manage all users from all the workspaces in the instance](#checking-all-the-users-in-the-instance) | ❌ | ✅ |
| [Make any user Super Admin](#make-the-user-super-admin) | ❌ | ✅ |
| [Restrict personal workspace of invited users](#allow-users-to-create-personal-workspace) | ❌ | ✅ |
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/instance.png" alt="Super Admin: Enterprise" />
</div>
## Super Admin features
### Access any workspace
If a user is a Super Admin then they can switch to any workspace created by any user in the instance from the dropdown on the top-left of dashboard that is used to switch between workspaces.
The dropdown will list all the workspaces including workspaces created by the Super Admin or Any User.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/workspacedrop.png" alt="Super Admin: Enterprise" />
</div>
### Create Edit or Delete apps from any user's personal workspace
Once the Super Admin accesses the workspace of any other user, they can create, edit or delete app on the workspace.
This also includes - modifying folders and importing, exporting, or cloning apps to any user's workspace.
### Archive/Unarchive Users
Super Admin can not only archive/unarchive users/admins on their workspace but also from the workspaces of any other user.
If a user is Super Admin, they just need to open the workspace in which they want to archive or unarchive a user. Then go to the **Workspace Settings** from the sidebar -> **Manage Users** -> **Archive/Unarchive** any user/admin
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/archive.png" alt="Super Admin: Enterprise" />
</div>
### Access ToolJet DB in any workspace
Super Admins have access to the database of any user's workspace - just like Super Admins can access any application in any workspace. They have full access to modify or create any table in the ToolJet DB of any workspace.
### Manage Workspace Settings (Groups/SSO/Workspace Variables)
Super Admins have all the privileges that an Admin of a workspace have, Super Admins can:
- **✅ Manage Groups**: Creating/Deleting/Updating a Group in any workspace
- **✅ Manage SSO**: Full control over General Settings, Password login and other SSO options
- **✅ Workspace Variables**: Adding, updating or deleting workspace variables
## Instance Settings
Only Super Admins can access the Instance Settings:
- **Manage All Users**
- **Manage Instance Settings**
### Checking all the users in the instance
**Manage All Users** can be used to check all the users that are there - altogether from all the workspaces in an instance.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/instancesettings.png" alt="Super Admin: Enterprise" />
</div>
### Archiving a user from workspace
Super Admins have the privilege to remove any user from any of the workspace they belong.
Super Admins can go to **Manage All Users** page, Under the **Workspaces** column they'll see the number of workspaces a user belongs to. Click on the **`View(n)`**, a modal will pop up that will have the list of **`n`** number the workspaces, click on the **Archive/Unarchive** button next to the workspace name to remove the user from the workspace.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/archwork.png" alt="Super Admin: Enterprise" />
</div>
### Make the user super admin
Super Admins can make any user as Super Admin or remove any Super Admin from the **Manage All Users** in the Instance Settings page.
Click on the **Edit** button next to any user, **Enable** the **Make the user Super Admin** option, and then **Save** it.
The user will become Super Admin and the Type column will update from **`workspace`** to **`instance`**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/makesa.png" alt="Super Admin: Enterprise" />
</div>
### Allow users to create personal workspace
When a user joins a workspace, they are provided with their own personal workspace and option to create new workspaces.
Super Admins can control this behavior from the Manage Instance Settings page, they can **toggle off** the option to **Allow personal workspace**. Now whenever a user joins a workspace they won't be provided a personal workspace nor they will be able to create a new workspace in the instance.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/superadmin/personal.png" alt="Super Admin: Enterprise" />
</div>

View file

@ -0,0 +1,39 @@
---
id: white-label
title: White Label
---
<div className='badge badge--primary heading-badge'>Available on: Enterprise Edition</div>
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)
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/white-label/applogo.png" alt="ToolJet - Enterprise - White label" width="500"/>
</div>
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/white-label/appeditor.png" alt="ToolJet - Enterprise - White label" width="500"/>
</div>
- **Favicon**
- **Page Title** (next to Favicon)
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/enterprise/white-label/favicon.png" alt="ToolJet - Enterprise - White label" width="500" />
</div>
## 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,5 @@
{
"label": "Actions Reference",
"position": 7,
"collapsed": true
}

View file

@ -0,0 +1,19 @@
---
id: close-modal
title: Close modal
---
Use this action to close the modal that is already shown.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/closemodal/closemodal2.png" alt="ToolJet - Action reference - Close modal" width="700" />
</div>

View file

@ -0,0 +1,98 @@
---
id: control-component
title: Control component (Component Specific Actions)
---
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, Disable, Visibility, Loading |
| Checkbox | Set checked |
| Color picker | Set color |
| Dropdown | Select option |
| File picker | Clear files |
| Kanban | Add card, Delete card, Move card, Update card data |
| Map | Set location |
| Modal | Show, Close |
| Multiselect | Select option, Deselect option, Clear selection |
| Radio button | Select option |
| Tabs | Set tab |
| Table | Set page, Select row, Deselect Row, Discard changes |
| Text | Set text, Set Visibility |
| Text Area | Set text, Clear |
| Text Input | Set text, Clear, Set Focus, Set Blur, Disable, Visibility |
:::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,18 @@
---
id: copy-to-clipboard
title: Copy to clipboard
---
Use this action to copy the text to the clipboard.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/copytoclipboard/copy2.png" alt="ToolJet - Action reference - Copy to clipboard" width="700" />
</div>

View file

@ -0,0 +1,42 @@
---
id: generate-file
title: Generate file
---
# Generate file
This action allows you to construct files on the fly and let users download it.
Presently, the only file type supported is `CSV`.
## Options
| Option | Description |
|--------|-------------|
| Type | Type of file to be generated |
| File name | Name of the file to be generated |
| Data | Data that will be used to construct the file. Its format will depend on the file type, as specified in the following section |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
### Data format for CSV
For `CSV` file type, the data field should be supplied with an array objects. ToolJet assumes that the keys of each of
these objects are the same and that they represent the column headers of the csv file.
Example:
```javascript
{{
[
{ name: 'John', email: 'john@tooljet.com' },
{ name: 'Sarah', email: 'sarah@tooljet.com' },
]
}}
```
Supplying the above snippet will generate a csv file which looks like this:
```csv
name,email
John,john@tooljet.com
Sarah,sarah@tooljet.com
```

View file

@ -0,0 +1,20 @@
---
id: go-to-app
title: Go to app
---
This action allows you to open any ToolJet application when an event occurs.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/gotoapp/gotoapp2.png" alt="ToolJet - Action reference - Open webpage" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: logout
title: Logout
---
This action allows you to log out of the application (ToolJet).
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/logout/logout2.png" alt="ToolJet - Action reference - Logout" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: open-webpage
title: Open webpage
---
You can use this action to open a webpage(on a new tab) for any event.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/open-webpage/open2.png" alt="ToolJet - Action reference - Open webpage" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: run-query
title: Run Query
---
This action allows you to fire queries when an event occurs.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/run-query/run-query2.png" alt="ToolJet - Action reference - Run Query" width="700" />
</div>

View file

@ -0,0 +1,59 @@
---
id: set-localstorage
title: Set localStorage
---
# Set localStorage
This action allows you to specify a `key` and its corresponding `value` to be stored in localStorage.
## Example: App that stores a name in localStorage and displays it on reload
1. Add an input field, button and a text as shown
<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
<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.
:::info
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/debounce.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
<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!
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference -Set local storage sample app](/img/actions/localstorage/6.png)
</div>

View file

@ -0,0 +1,20 @@
---
id: set-page-variable
title: Set page variable
---
Page variables can only be accessed within a page on which they are created, unlike normal variables that can be accessed throughout the application.
Use this action to create a variable and assign a `value` to it in the [Multipage Apps](/docs/tutorial/pages).
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/page/setpagevar2.png" alt="ToolJet - Action reference - Switch page" width="600"/>
</div>

View file

@ -0,0 +1,24 @@
---
id: set-table-page
title: Set Table Page
---
Use this action to change the page index in the table widget.
## Options
| Option | Description |
|--------|-------------|
| Table | Select table from the dropdown |
| Page Index | Numerical value for the page index. ex: `{{2}}` |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::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'}}>
<img className="screenshot-full" src="/img/actions/settablepage/page2.png" alt="ToolJet - Action reference - Open webpage" width="700" />
</div>

View file

@ -0,0 +1,24 @@
---
id: set-variable
title: Set variable
---
This action allows you to create a variable and assign a `value` to it.
## Options
| Option | Description |
|--------|-------------|
| Key | Name(String) of the variable through which you can access the value |
| Value | A value can be a string, number, boolean expression, array, or object |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::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'}}>
<img className="screenshot-full" src="/img/actions/setvar/setvar2.png" alt="ToolJet - Action reference -Set variable" width="700" />
</div>

View file

@ -0,0 +1,23 @@
---
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 **Error**.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/show-alert/alert2.png" alt="ToolJet - Action reference - Show Alert" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: show-modal
title: Show modal
---
Use this action to show the modal for an event.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/showmodal/showmodal2.png" alt="ToolJet - Action reference - Show modal" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: switch-page
title: Switch Page
---
Use this action with different events to switch to a different page in the [Multipage App](/docs/tutorial/pages).
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/page/switchpage2.png" alt="ToolJet - Action reference - Switch page" width="700"/>
</div>

View file

@ -0,0 +1,18 @@
---
id: unset-page-variable
title: Unset page variable
---
Use this action to clear the variable that was created using the set page variable action.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::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'}}>
<img className="screenshot-full" src="/img/actions/page/unsetpagevar2.png" alt="ToolJet - Action reference - Switch page" width="600"/>
</div>

View file

@ -0,0 +1,23 @@
---
id: unset-variable
title: Unset variable
---
This action allows you to remove the variable variable that was created using the set variable action.
## Options
| Option | Description |
|--------|-------------|
| Key | Name(String) of the variable through which you can access the value |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::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'}}>
<img className="screenshot-full" src="/img/actions/unsetvar/unsetvar2.png" alt="ToolJet - Action reference -Unset variable" width="700" />
</div>

View file

@ -0,0 +1,55 @@
---
id: canvas
title: Canvas
---
Canvas is the center area of the ToolJet app builder where the application is built. You arrange the **components** by dragging them from the Components library(right-sidebar).
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/canvas/canvas.png" alt="App Builder: Canvas"/>
</div>
### Arrange Components
All the components are fully interactive in editor mode - to prevent interaction you can **click and hold** the **[Component Handle](docs/app-builder/components-library)** to change component's position.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/canvas/arrange.png" alt="App Builder: Canvas"/>
</div>
### Resize Components
Components on the canvas can be resized from the edges.
You can precisely set the position of selected components using keyboard arrow keys after clicking the component handle.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/canvas/resize.gif" alt="App Builder: Canvas"/>
</div>
### Group Components
ToolJet comes with flexible components to group other components together, such as **Container** and **Form**. When you drag and drop components in containers/forms they create a group of nested components. All components can be nested in this way.
### Hide or Disable Components
Hide or Disable a component by setting its **Visibility** or **Disabled** property to `true`. Click on the component handle to open **config inspector** on right side. These values can also evaluate to true based on a truthy value. For example, you can use the property of one component to toggle the Visibility property of another component dynamically, you just need to write a conditional statement.
For example: We want to disable a button when a checkbox is checked so we can simple use `{{components.checkbox1.value}}` in **Disable** property of the button. `{{components.checkbox1.value}}` evaluates to `true` when the checkbox is checked, and false when unchecked.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/canvas/hide.gif" alt="App Builder: Canvas"/>
</div>
### Clone Components
You can clone existing components on the canvas by **cmd/ctrl + d**. Check other **[Keyboard Shortcuts](/docs/tutorial/keyboard-shortcuts)**

View file

@ -0,0 +1,68 @@
---
id: left-sidebar
title: Left-sidebar
---
Left-sidebar has the following options:
- **[Pages](#pages)**
- **[Inspector](#inspector)**
- **[Datasources Manager](#datasources-manager)**
- **[Debugger](#debugger)**
- **[Theme switch](#theme-switch)**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/leftsidebar/leftsidebar.png" alt="App Builder: Left-sidebar"/>
</div>
## Pages
Pages allows you to have multiple pages in a single application, making your ToolJet applications more robust and user-friendly.
Check the detailed documentation for **[Pages](/docs/tutorial/pages)**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/leftsidebar/pages.png" alt="App Builder: Left-sidebar"/>
</div>
## Inspector
The Inspector can be used to inspect the data of the **queries**, properties and values of the **components** that are there on the canvas, ToolJet's global variables and the variables that have been set by the user.
Check the detailed guide on **[using Inspector](/docs/how-to/use-inspector)**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/leftsidebar/inspector.png" alt="App Builder: Left-sidebar"/>
</div>
## Datasources Manager
Datasources Manager is used to connect or remove the datasources.
Check the detailed **[Datasources documentation](/docs/data-sources/overview)**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/leftsidebar/datasources.png" alt="App Builder: Left-sidebar"/>
</div>
## Debugger
The debugger captures errors that happens while running the queries. For example, when a database query fails due to the unavailability of a database or when a REST API query fails due to an incorrect URL, the errors will be displayed on the debugger. The debugger also displays relevant data related to the error along with the error message.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/leftsidebar/debugger.png" alt="App Builder: Left-sidebar"/>
</div>
## Theme switch
Switch ToolJet into light or dark mode from this button.

View file

@ -0,0 +1,20 @@
---
id: overview
title: Overview
---
# App-Builder: Overview
ToolJet's App Builder allows you to build applications. ToolJet's app builder has the following major components:
- **[Toolbar](/docs/app-builder/toolbar)**: configure app settings
- **[Canvas](/docs/app-builder/canvas)**: Arrange the components to build the interface of app
- **[Left-sidebar](/docs/app-builder/left-sidebar)**: Add **[pages](/docs/tutorial/pages)**, **[datasources](/docs/data-sources/overview)**, **[inspect](/docs/how-to/use-inspector)** the components, queries or variables, and **[debug](#debugger)** the errors.
- **[Components library](/docs/app-builder/components-library)**(right sidebar): Drag any component or modify the property or styling
- **[Query Panel](/docs/app-builder/query-panel)**: Create, edit or manage the queries
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/builder.png" alt="App Builder: Overview"/>
</div>

View file

@ -0,0 +1,177 @@
---
id: query-panel
title: Query Panel
---
The Query Panel is present at the bottom of the app-builder, this is where you create queries to interact with connected datasources. You can perform API requests, query **[databases](/docs/data-sources/overview)**, or **[transform](/docs/tutorial/transformations)** or manipulate data with **[JavaScript](/docs/data-sources/run-js)** & **[Python](/docs/data-sources/run-py)**.
The Query Panel has two sections:
- **[Query Manager](#query-manager)** on the right that includes a list of all the created queries
- **[Query Editor](#query-editor)** is used to configure the selected query
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/querypanel.png" alt="App Builder: Component library- right sidebar"/>
</div>
## Query Manager
Query Manager will list all the queries that has been created in the application. Query Manager is used to:
### Search
On the top of the query manager is search box that can be used to search for a specific query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/search.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Add
Add button is used to add more queries in the application. When Add button is clicked, the Query Editor will show you a list of options for creating a query from: **Rest API**, connected **[datasources](/docs/data-sources/overview)**, **[ToolJet Database](/docs/tooljet-database)**, **[JavaScript Code](/docs/data-sources/run-js)**, **[Python Code](/docs/data-sources/run-py)** or Add a new datasource.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/add.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Delete
Delete button will delete the selected query, the button will only show up when you hover over the query name.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/delete.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Edit
Edit button is used edit the name of the selected query, the button will only show up when you hover over the query name.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/edit.png" alt="App Builder: Component library- right sidebar"/>
</div>
## Query Editor
Query editor used to configure the query parameters, preview or transform the data return by the query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/editor.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Topbar
On the top of the query panel there are a few options:
#### Query Name editor
Edit the name of the query by clicking on the edit button next to the default query name.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/nameedit.png" alt="App Builder: Component library- right sidebar"/>
</div>
#### Preview
Preview gives you a quick look at the data returned by the query without triggering the query in the app.
The Preview of data is returned in two different formats:
**Raw**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/raw.png" alt="App Builder: Component library- right sidebar"/>
</div>
**JSON**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/json.png" alt="App Builder: Component library- right sidebar"/>
</div>
#### Save
Save is used to save the changes whenever a change is made in query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/save.png" alt="App Builder: Component library- right sidebar"/>
</div>
#### Run
Run is used to trigger the query, running the query will interact with the application unlike `Preview`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/run.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Query Parameters
Query Parameters are the values required for the query to return a response from the server. Parameters include **endpoints**, **methods**, or **operations**. Query Parameters are different for each datasource.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/params.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Transformation
Transformations can be enabled on queries to transform the query results. ToolJet allows you to transform the query results using two programming languages JavaScript & Python. Check the detailed documentation on **[Transformations](/docs/tutorial/transformations)**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/transform.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Advanced options
#### Run this query on application load?
Enabling this option will fire the query every time the app is loaded.
#### Request confirmation before running the query?
Enabling this option show a confirmation modal to confirm `Yes` or `No` if you want to fire that query.
#### Run this query on application load?
Enabling this option show a success toast notification when the query is successfully triggered.
#### Event Handlers
Event Handler are used to add some action when a particular event happens. You can add event handlers to the query for the following events:
- **Query Success**
- **Query Failure**
:::info
Learn more about [Event Handlers and Actions](/docs/widgets/overview#component-event-handlers).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/advanced.png" alt="App Builder: Component library- right sidebar"/>
</div>

View file

@ -0,0 +1,29 @@
---
id: components-library
title: Components Library
---
The **Components Library** on the right sidebar contains all of the available components. Use this to drag-and-drop a component from the library to the canvas. It organizes components into sections and you can enter a search term to quickly find a component you need.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/rightsidebar/rightsidebar.png" alt="App Builder: Component library- right sidebar"/>
</div>
:::tip
Check the **[Components Catalog](/docs/widgets/overview)** here to know more about specific component.
:::
## Component Config Inspector
The Component Config Inspector is also called as component inspector. It contains all the available settings for the selected component and is where you **set values**, **update component names**, and **create event handlers**. The Compoenent Inspector organizes settings into different sections, such as **Property** and **Styles**.
To open the Component Config Inspector, click on the component handle that is present on the top of the component including **⚙️ + Component Name** and the component inspector will open up on the right side:
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/rightsidebar/component-inspector.gif" alt="App Builder: Component library- right sidebar"/>
</div>

View file

@ -0,0 +1,133 @@
---
id: toolbar
title: Toolbar
---
Toolbar is present at the top of the app-builder, and is used to configure the app settings.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/toolbar.png" alt="App Builder: Toolbar"/>
</div>
### App name
App name can be edited from the left side of the toolbar next to the ToolJet logo.
When a new app is created, by default its name is set to **Untitled app**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/name.png" alt="App Builder: Toolbar"/>
</div>
### Global Settings
To configure the app's global settings, click on the kebab menu(three vertical dots) on the left of the app name. Global settings include:
- **Hide heaeder for launched apps**: Toggle this on to the hide the tooljet's header when the applications are launched
- **Maintenance mode**: Toggle this on to put the application in maintenance mode. When in **maintenance mode**, on launching the app, the user will get an error message that **the app is under maintenance**.
- **Max width of canvas**: Modify the width of the canvas in **px** or **%**. The default width is 1292 px.
- **Max height of canvas**: Modify the width of the canvas in **px** or **%**. The default height is 2400 px and currently it is the maximum height limit.
- **Background color of canvas**: Enter the hex color code or choose a color from the picker to change the background color of the canvas. You can also click on the **Fx** to programmatically set the value.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/globalset.png" alt="App Builder: Toolbar"/>
</div>
### Desktop or Mobile layout
Switch the canvas mode in Mobile or Desktop layout from the toolbar.
#### Adding existing component to mobile layout
Click on the component handle to open component config inspector on the right side. Scroll down to the **Layout** section and enable Mobile Layout. The width of the widget will be adjusted to fit the Mobile Layout.
#### Adding a new component to mobile layout
Switch the layout to mobile by clicking the button on the toolbar. Drag and drop a component to the canvas. This widget will not be shown on desktop layout unless **Show on desktop** is enabled from the component config inspector.
:::info
Width of the component will be automatically adjusted to fit the screen while viewing the application in app viewer.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/view.png" alt="App Builder: Toolbar"/>
</div>
### Undo or Redo
Use the undo or redo buttons from the toolbar to undo or redo any change on the canvas.
You can also **[Keyboard Shortcuts](/docs/tutorial/keyboard-shortcuts)** to perform such actions.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/undo.png" alt="App Builder: Toolbar"/>
</div>
### Version Manager
Create or Remove Versions of the applications from the Version Manager. You can also edit the version name from the edit button.
When many developers are working on an app, **Versioning** allows them to save their own version of the app. This also prevents developers from overwriting the other developer's work.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/version.png" alt="App Builder: Toolbar"/>
</div>
### Comments
Comment anywhere on the canvas and collaborate with other users in the workspace. Click on the comments button to enable it and then drop comment anywhere on the canvas.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/comments.png" alt="App Builder: Toolbar"/>
</div>
### Share
Share your applications with a unique URL generated automatically or edit the URL slug to personalize it.
- When **Make the application public** is off and URL is shared then the users will have to login to ToolJet to use the application. Toggle on the option then anyone on the internet will be able to access the application without logging in to ToolJet.
- ToolJet generates the **Embedded link** which can be used to embed application on the webpages.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/share.png" alt="App Builder: Toolbar"/>
</div>
### Preview
Clicking on **Preview** button will open up the currently opened version of the app in the new tab. This is really handy when the app developer wants to immediately check the app preview in production.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/preview.png" alt="App Builder: Toolbar"/>
</div>
### Release
Release the app to publish the current version of the app and push the changes into the production.
:::caution
ToolJet will block editing of the Released version of an app and will display a prompt to create a new version to make the changes. This is to prevent accidentally pushing an unfinished app to the live version.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/toolbar/release.png" alt="App Builder: Toolbar"/>
</div>

View file

@ -0,0 +1,5 @@
{
"label": "Contributing Guide",
"position": 11,
"collapsed": true
}

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