Merge pull request #8340 from ToolJet/platform/v10.x

* [docs]:update quickstart guide v2 (#8058)

* revamp quickstart guide and add topics to ToolJet concepts

* add new guides using floik

* add new items to tooljet concepts

* update writing custom code concept

* make UI enhancements and update the getting started guide

* update tooljet concepts and add new topics

* merge with dev and misc enhancements

* create render

* enhance formatting in access values concept

* updates to quickstart guide and concepts

* fix proofreading errords and update docs

* revert versions and doc settings

* add platform overview page and fix issues with floik demo sizing

* add padding to badges and update workflows screenshot

* add latest docs to the previous version

* remove versions to preview on render

* remove old getting started guide from next and 2.25

* add homepage slug

* [docs] git sync - docs v2.24 (#8078)

* [docs] gitsync

* [docs]git sync

* updates after review

* added flow diagram and use cases

* changes after review

* Update docs/docs/gitsync.md

* note for workspace constants

* Added docs v2.24.0

* bump version

* [Imp] Auto-removing query params from redirect urls (While login to the application) (#8128)

* add: added query params to logout urls

* add: added support for sso login

* fix: page handle not updated after rename

* [fix] Rendering all apps inside a folder after the app rename. (#8133)

* fix: rendering all apps inside a folder after app rename

* fix: Breadcrumbs path not contains query params

* [fix] Sometimes the workspace modal CTA is in disabled state even if the values are correct (#8299)

* fix: sometimes the workspace modal CTA is in disabled state even if the values are accepted

* fix: erorr message

* Fix MariaDB connection issues (#8235)

* Add default connection limit for mariadb test connection in backend

* map connectionLimit to connection_limit

* remove log

* remove comments

* fix: mariadbPool causes server crash in case of unknown db

* fix: prev details getting replicated in next selected ds

* Save button on Datasource changes not enabled (#8171)

* fixes: disabled state of ds changes on header values

* add check for editor headers

* fix: constants resolution on saved datasources (#8223)

* Fixed websocket unavailable issue when the user tries to release the version before the connection is established (#8305)

* Revamp Global Data sources (#8274)

* changes: listing of data sources

* add: plugins to ds list

* review fixes

* fixes

* fix: placeholder values for encrypted fields

* fixes

* review changes

* added missing line (#8349)

* Fix for handling 'go to app' event for previously added unreleased apps and new apps (#8169)

* fix go to app event for unreleased apps

* updates

* fix: added 401 check (#8351)

* [chore] Node-module vulnerabilities (#8226)

* started working on node upgrade to 18.18.2

* testing ci

* updated ci node version

* updated action code

* deleted all package-lock.json files

* deleted and ovverrided some packages

* deleted and fixed server & frontend vulnerabilities

* updated firestore version

* fix: ws type issue

* fix: upgraded lerna version

* regenerated package-lock.json files again

* regenerated marketplace lock file

* updated node version in other ci and docker files

* update: lock file plugin side

* updated the npm version in docker & ci files

* removed unused imports from events file

* removed dependency-review action

* updated some packages

* tried to go with current node-module of jest. had to upgrade

* fix: deprecated function usage - ts-jest

* fix: server directory lint issues

* fixed login page issue after router-dom upgrade

* updated import/no-unresolved rule to ignore import errors of  react-loading-skeleton, react-spring packages

* fix: cypress node version & package-lock issue

* regenerated cli package-lock.json

* fix: new webpack version might cause runtime issues (had issues with enterprise). lets use old version only

* fix: form-data docker issue

* removed comment

* Modify failing test cases (#8372)

* package-lock for marketplace (#8391)

* Fixes UI and test connection issues (#8377)

* fixes: testconnection bugs

* fixes

* revert: package-lock

* add: new line

* url redirect fix

* Added test user on seeding (#8393)

* Added test user on seeding

* fix

* fix

* fix

* Updated the cypress spec with node upgrade changes (#8405)

* update cypress specs

* update the data source count values

---------

Co-authored-by: Karan Rathod <karan.altcampus@gmail.com>
Co-authored-by: Shubhendra Singh Chauhan <withshubh@gmail.com>
Co-authored-by: Kavin Venkatachalam <50441969+kavinvenkatachalam@users.noreply.github.com>
Co-authored-by: Muhsin Shah C P <muhsinshah21@gmail.com>
Co-authored-by: Anantshree Chandola <anantshreechandola23@gmail.com>
Co-authored-by: vjaris42 <vjy239@gmail.com>
Co-authored-by: Ajith KV <ajith.jaban@gmail.com>
Co-authored-by: Mekhla Asopa <59684099+Mekhla-Asopa@users.noreply.github.com>
This commit is contained in:
Midhun G S 2023-12-26 17:43:39 +05:30 committed by GitHub
commit d1c00dbee2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
436 changed files with 97924 additions and 152664 deletions

View file

@ -38,10 +38,10 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- name: Use Node.js 18.3.0
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
with:
node-version: 18.3.0
node-version: 18.18.2
- name: Cache node modules
uses: actions/cache@v3
@ -68,10 +68,10 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- name: Use Node.js 18.3.0
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
with:
node-version: 18.3.0
node-version: 18.18.2
- name: Cache node modules
uses: actions/cache@v3
@ -93,7 +93,7 @@ jobs:
unit-test:
runs-on: ubuntu-latest
needs: build
container: node:18.3.0-buster
container: node:18.18.2-buster
services:
postgres:
image: postgres
@ -131,7 +131,7 @@ jobs:
e2e-test:
runs-on: ubuntu-latest
needs: build
container: node:18.3.0-buster
container: node:18.18.2-buster
services:
postgres:
image: postgres

View file

@ -20,7 +20,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 18.3.0
node-version: 18.18.2
- name: Set up Docker
uses: docker-practice/actions-setup-docker@master

View file

@ -20,7 +20,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 18.3.0
node-version: 18.18.2
- name: Set up Docker
uses: docker-practice/actions-setup-docker@master

2
.nvmrc
View file

@ -1 +1 @@
v18.3.0
v18.18.2

View file

@ -1 +1 @@
2.26.2
2.27.0

12470
cli/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -70,7 +70,7 @@ Cypress.Commands.add("apiCreateApp", (appName = "testApp") => {
url: "http://localhost:3000/api/apps",
headers: {
"Tj-Workspace-Id": Cypress.env("workspaceId"),
Cookie: `tj_auth_token=${cookie.value}`,
Cookie: `tj_auth_token = ${cookie.value}`,
},
body: {
created_at: "",
@ -212,3 +212,27 @@ Cypress.Commands.add("userInviteApi", (userName, userEmail) => {
});
});
});
Cypress.Commands.add("addQueryApi", (queryName, query, dataQueryId) => {
cy.getCookie("tj_auth_token").then((cookie) => {
const headers = {
"Tj-Workspace-Id": Cypress.env("workspaceId"),
Cookie: `tj_auth_token=${cookie.value}`,
};
cy.request({
method: "PATCH",
url: `http://localhost:3000/api/data_queries/${dataQueryId}`,
headers: headers,
body: {
name: queryName,
options: {
mode: "sql",
transformationLanguage: "javascript",
enableTransformation: false,
query: query,
},
},
}).then((patchResponse) => {
expect(patchResponse.status).to.equal(200);
});
});
});

View file

@ -7,7 +7,7 @@ export const s3Text = {
alertRegionIsMissing: "Region is missing",
cypressAwsS3: "cypress-aws-s3",
placeholderEnterAccessKey: "Enter access key",
placeholderEnterSecretKey: "Enter secret key",
placeholderEnterSecretKey: "**************",
labelRegion: "Region",
region: "N. california",
alertInvalidUrl: "Invalid URL",

View file

@ -3,5 +3,5 @@ export const bigqueryText = {
cypressBigQuery: "cypress-bigquery",
errorInvalidEmailId:
"The incoming JSON object does not contain a client_email field",
placehlderPrivateKey: "Enter JSON private key for service account",
placehlderPrivateKey: "**************",
};

View file

@ -5,7 +5,7 @@ export const dataSourceText = {
allDataSources: "All data sources (41)",
allDatabase: "Databases (17)",
allApis: "APIs (20)",
allCloudStorage: "Cloud Storage (4)",
allCloudStorage: "Cloud Storages (4)",
pluginsLabelAndCount: "Plugins (0)",
postgreSQL: "PostgreSQL",

View file

@ -5,7 +5,7 @@ export const dynamoDbText = {
accessKey: "Access key",
placeHolderAccessKey: "Enter access key",
secretKey: "Secret key",
placeholderSecretKey: "Enter secret key",
placeholderSecretKey: "**************",
errorMissingRegion: "Missing region in config",
errorInvalidToken: "The security token included in the request is invalid.",

View file

@ -3,7 +3,7 @@ export const firestoreText = {
cypressFirestore: "cypress-firestore",
labelPrivateKey: "Private key",
privateKey: "Private key",
placeholderPrivateKey: "Enter private key",
placeholderPrivateKey: "**************",
errorGcpKeyCouldNotBeParsed:
"GCP key could not be parsed as a valid JSON object",

View file

@ -5,7 +5,7 @@ export const postgreSqlText = {
allDataSources: "All data sources (43)",
allDatabase: "Databases (19)",
allApis: "APIs (20)",
allCloudStorage: "Cloud Storage (4)",
allCloudStorage: "Cloud Storages (4)",
postgreSQL: "PostgreSQL",
labelHost: "Host",

View file

@ -4,6 +4,6 @@ export const redisText = {
errorMaxRetries:
'Reached the max retries per request limit (which is 1). Refer to "maxRetriesPerRequest" option for details.',
errorPort: "Port should be >= 0 and < 65536. Received 108299.",
errorPort: "Port should be >= 0 and < 65536. Received type number (108299).",
errorInvalidUserOrPassword: "WRONGPASS invalid username-password pair",
};

View file

@ -10,7 +10,6 @@ import {
closeDSModal,
deleteDatasource,
addQuery,
addQueryN,
verifyValueOnInspector,
} from "Support/utils/dataSource";
import { dataSourceSelector } from "Selectors/dataSource";
@ -38,12 +37,8 @@ describe("Global Datasource Manager", () => {
cy.viewport(1200, 1300);
});
before(() => {
cy.apiLogin();
cy.apiCreateApp();
cy.openApp();
cy.renameApp(data.appName);
cy.dragAndDropWidget("Table", 250, 250);
cy.get(commonSelectors.editorPageLogo).click();
cy.defaultWorkspaceLogin();
cy.apiCreateApp(data.appName);
addNewUserMW(data.firstName, data.email);
logout();
});
@ -72,7 +67,7 @@ describe("Global Datasource Manager", () => {
);
cy.get(dataSourceSelector.querySearchBar)
.invoke("attr", "placeholder")
.should("eq", "Search Databases");
.should("eq", "Search data sources");
cy.get(dataSourceSelector.apiLabelAndCount)
.verifyVisibleElement("have.text", dataSourceText.allApis)
@ -81,20 +76,14 @@ describe("Global Datasource Manager", () => {
"have.text",
" APIs"
);
cy.get(dataSourceSelector.querySearchBar)
.invoke("attr", "placeholder")
.should("eq", "Search APIs");
cy.get(dataSourceSelector.cloudStorageLabelAndCount)
.verifyVisibleElement("have.text", dataSourceText.allCloudStorage)
.click();
cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement(
"have.text",
" Cloud Storage"
" Cloud Storages"
);
cy.get(dataSourceSelector.querySearchBar)
.invoke("attr", "placeholder")
.should("eq", "Search Cloud Storage");
cy.get(dataSourceSelector.pluginsLabelAndCount)
.verifyVisibleElement("have.text", dataSourceText.pluginsLabelAndCount)
@ -103,9 +92,6 @@ describe("Global Datasource Manager", () => {
"have.text",
" Plugins"
);
cy.get(dataSourceSelector.querySearchBar)
.invoke("attr", "placeholder")
.should("eq", "Search Plugins");
cy.get('[data-cy="added-ds-label"]').should(($el) => {
expect($el.contents().first().text().trim()).to.eq("Data sources added");
@ -178,6 +164,7 @@ describe("Global Datasource Manager", () => {
deleteDatasource(`cypress-${data.dsName1}-postgresql1`);
});
it("Should verify the Datasource connection and query creation using global data source", () => {
selectAndAddDataSource(
"databases",
@ -198,10 +185,7 @@ describe("Global Datasource Manager", () => {
);
cy.wait("@datasource");
cy.get(commonSelectors.globalDataSourceIcon).click();
cy.get(commonSelectors.dashboardIcon).click();
navigateToAppEditor(data.appName);
cy.openApp();
pinInspector();
addQuery(
@ -223,24 +207,22 @@ describe("Global Datasource Manager", () => {
.should("be.visible")
.and("have.text", "+ Add new Data source");
cy.get(".p-2 > .tj-base-btn").click();
cy.get('[data-cy="databases-datasource-button"]').should("be.visible");
selectAndAddDataSource(
"databases",
dataSourceText.postgreSQL,
data.dsName2
cy.apiCreateGDS(
"http://localhost:3000/api/v2/data_sources",
`cypress-${data.dsName2}-postgresql`,
"postgresql",
[
{ key: "host", value: Cypress.env("pg_host") },
{ key: "port", value: 5432 },
{ key: "database", value: Cypress.env("pg_user") },
{ key: "username", value: Cypress.env("pg_user") },
{ key: "password", value: Cypress.env("pg_password"), encrypted: true },
{ key: "ssl_enabled", value: false, encrypted: false },
{ key: "ssl_certificate", value: "none", encrypted: false },
]
);
cy.intercept("GET", "api/v2/data_sources").as("datasource");
fillConnectionForm(
{
Host: Cypress.env("pg_host"),
Port: "5432",
"Database Name": Cypress.env("pg_user"),
Username: Cypress.env("pg_user"),
Password: Cypress.env("pg_password"),
},
".form-switch"
);
cy.wait("@datasource");
navigateToManageGroups();
cy.get(groupsSelector.appSearchBox).click();
@ -265,7 +247,7 @@ describe("Global Datasource Manager", () => {
it("Should validate the user's global data source permissions on apps created by admin", () => {
logout();
cy.apiLogin(data.email, "password");
cy.visit('/my-workspace')
cy.visit("/my-workspace");
cy.get(commonSelectors.globalDataSourceIcon).should("not.exist");
@ -281,8 +263,7 @@ describe("Global Datasource Manager", () => {
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
verifyValueOnInspector("table_preview", "7 items ");
cy.get('[data-cy="show-ds-popover-button"]').click();
addQueryN(
addQuery(
"student_data",
`SELECT * FROM student_data;`,
`cypress-${data.dsName2}-postgresql`
@ -293,16 +274,14 @@ describe("Global Datasource Manager", () => {
"student_data "
);
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
verifyValueOnInspector("student_data", "8 items ");
verifyValueOnInspector("student_data", "4 items ");
});
it("Should verify the query creation and scope changing functionality.", () => {
data.appName = `${fake.companyName}-App`;
logout();
cy.apiLogin(data.email, "password");
cy.visit('/my-workspace')
cy.apiCreateApp(data.appName);
cy.openApp();
cy.dragAndDropWidget("Table", 250, 250);
addQuery(
"table_preview",

View file

@ -96,7 +96,7 @@ describe("Data sources", () => {
);
fillDataSourceTextField(
"Key",
"Enter your key",
"**************",
Cypress.env("cosmosdb_key")
);

View file

@ -133,7 +133,7 @@ describe("Data source Elasticsearch", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("elasticsearch_password")
);
@ -163,7 +163,7 @@ describe("Data source Elasticsearch", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
"elasticsearch_password"
);
@ -173,7 +173,7 @@ describe("Data source Elasticsearch", () => {
);
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("elasticsearch_password")
);

View file

@ -136,7 +136,7 @@ describe("Data sources", () => {
);
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("mariadb_password")
);

View file

@ -129,7 +129,7 @@ describe("Data sources MySql", () => {
);
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("mysql_password")
);
@ -169,7 +169,7 @@ describe("Data sources MySql", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("mysql_password")
);

View file

@ -143,7 +143,7 @@ describe("Data sources", () => {
);
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("pg_password")
);

View file

@ -123,7 +123,7 @@ describe("Data source Redis", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("redis_password")
);
@ -156,7 +156,7 @@ describe("Data source Redis", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
"redis_password"
);
@ -168,7 +168,7 @@ describe("Data source Redis", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("redis_password")
);

View file

@ -109,7 +109,7 @@ describe("Data source SMTP", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("smtp_password")
);

View file

@ -103,7 +103,7 @@ describe("Data sources", () => {
);
cy.get('[data-cy="connection-alert-text"]').should(
"have.text",
"Network error. Could not reach Snowflake."
"Invalid account. The specified value must be a valid subdomain string."
);
deleteDatasource(`cypress-${data.lastName}-snowflake`);
});
@ -124,7 +124,7 @@ describe("Data sources", () => {
);
fillDataSourceTextField(
"Password",
"Enter password",
"**************",
Cypress.env("snowflake_password")
);
fillDataSourceTextField(

View file

@ -141,7 +141,7 @@ describe("Data sources", () => {
fillDataSourceTextField(
postgreSqlText.labelPassword,
"Enter password",
"**************",
Cypress.env("sqlserver_password")
);

View file

@ -100,17 +100,20 @@ describe("Redirection error pages", () => {
cy.visit(`http://localhost:8082/applications/${data.slug}`);
cy.get(commonSelectors.modalHeader).verifyVisibleElement(
"have.text",
"URL unavailable"
"App URL Unavailable"
);
cy.get(commonSelectors.modalDescription).verifyVisibleElement(
"have.text",
"This URL is not accessible because it has not been released yet. Please either release it or contact admin for access."
'The app URL is currently unavailable because the app has not been released. Please either release it or contact admin for access.'
);
cy.get('[data-cy="open-app-button"]').verifyVisibleElement("have.text", "Open app")
cy.get(commonSelectors.backToHomeButton).verifyVisibleElement(
"have.text",
"Back to home page"
);
cy.url().should("eq", "http://localhost:8082/error/url-unavailable");
cy.url().should("eq", `http://localhost:8082/error/url-unavailable?appSlug=${data.slug}`);
cy.get(commonSelectors.backToHomeButton).click();
cy.get(commonSelectors.pageSectionHeader).should("be.visible");
@ -127,6 +130,8 @@ describe("Redirection error pages", () => {
"have.text",
"You dont have access to this app. Kindly contact admin to know more."
);
// cy.get('[data-cy="open-app-button"]').verifyVisibleElement("have.text", "Open app")
cy.get(commonSelectors.backToHomeButton).verifyVisibleElement(
"have.text",
"Back to home page"
@ -157,17 +162,17 @@ describe("Redirection error pages", () => {
cy.visit(`http://localhost:8082/applications/${data.slug}`);
cy.get(commonSelectors.modalHeader).verifyVisibleElement(
"have.text",
"URL unavailable"
"App URL Unavailable"
);
cy.get(commonSelectors.modalDescription).verifyVisibleElement(
"have.text",
"This URL is not accessible because it has not been released yet. Please either release it or contact admin for access."
'The app URL is currently unavailable because the app has not been released. Please either release it or contact admin for access.'
);
cy.get(commonSelectors.backToHomeButton).verifyVisibleElement(
"have.text",
"Back to home page"
);
cy.url().should("eq", "http://localhost:8082/error/url-unavailable");
cy.url().should("eq", `http://localhost:8082/error/url-unavailable?appSlug=${data.slug}`);
cy.get(commonSelectors.backToHomeButton).click();
cy.get(commonSelectors.pageSectionHeader).should("be.visible");
});

View file

@ -28,7 +28,7 @@ export const deleteComponentAndVerify = (widgetName) => {
});
cy.verifyToastMessage(
`[class=go3958317564]`,
"Component deleted! ( + Z to undo)"
"Component deleted! (ctrl + Z to undo)"
);
cy.notVisible(commonWidgetSelector.draggableWidget(widgetName));
};

View file

@ -72,33 +72,44 @@ export const closeDSModal = () => {
});
};
export const addQuery = (queryName, query, dbName) => {
export const addQueryN = (queryName, query, dbName) => {
cy.get("body").then(($body) => {
if ($body.find('[data-cy="gds-querymanager-search-bar"]').length > 0) {
cy.clearAndType('[data-cy="gds-querymanager-search-bar"]', `${dbName}`);
}
});
cy.intercept("POST", "http://localhost:3000/api/data_queries").as(
"createQuery"
);
cy.get(`[data-cy="${dbName}-add-query-card"] > .text-truncate`).click();
cy.get('[data-cy="query-rename-input"]').clear().type(queryName);
cy.forceClickOnCanvas();
cy.get(dataSourceSelector.queryInputField)
.realMouseDown({ position: "center" })
.realType(" ");
cy.get(dataSourceSelector.queryInputField).clearAndTypeOnCodeMirror(query);
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
cy.wait("@createQuery").then((interception) => {
const dataQueryId = interception.response.body.id;
cy.visit("/my-workspace");
cy.addQueryApi(queryName, query, dataQueryId);
cy.openApp();
});
};
export const addQueryN = (queryName, query, dbName) => {
export const addQuery = (queryName, query, dbName) => {
cy.get('[data-cy="show-ds-popover-button"]').click();
cy.get(".css-1rrkggf-Input").type(`${dbName}`);
cy.intercept("POST", "http://localhost:3000/api/data_queries").as(
"createQuery"
);
cy.contains(`[id*="react-select-"]`, dbName).click();
cy.get('[data-cy="query-rename-input"]').clear().type(queryName);
cy.get(dataSourceSelector.queryInputField)
.realMouseDown({ position: "center" })
.realType(" ");
cy.get(dataSourceSelector.queryInputField).clearAndTypeOnCodeMirror(query);
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
cy.wait("@createQuery").then((interception) => {
const dataQueryId = interception.response.body.id;
cy.visit("/my-workspace");
cy.addQueryApi(queryName, query, dataQueryId);
cy.openApp();
});
};
export const verifyValueOnInspector = (queryName, value) => {
@ -119,4 +130,4 @@ export const verifyValueOnInspector = (queryName, value) => {
);
}
});
};
};

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates apt-u
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install 18.3.0
nvm install 18.18.2
sudo ln -s "$(which node)" /usr/bin/node
sudo ln -s "$(which npm)" /usr/bin/npm
@ -74,7 +74,7 @@ mv /tmp/.env ~/app/.env
mv /tmp/setup_app ~/app/setup_app
sudo chmod +x ~/app/setup_app
npm install -g npm@8.11.0
npm install -g npm@9.8.1
# Building ToolJet app
npm install -g @nestjs/cli

View file

@ -1,9 +1,9 @@
# pull official base image
FROM node:18.3.0-buster
FROM node:18.18.2-buster
ENV NODE_ENV=development
RUN npm i -g npm@8.11.0
RUN npm i -g npm@9.8.1
# set working directory
WORKDIR /app

View file

@ -1,7 +1,7 @@
# pull official base image
FROM node:18.3.0-buster
FROM node:18.18.2-buster
RUN npm i -g npm@8.11.0
RUN npm i -g npm@9.8.1
# set working directory
WORKDIR /app

View file

@ -1,4 +1,4 @@
FROM node:18.3.0-buster AS builder
FROM node:18.18.2-buster AS builder
# Fix for JS heap limit allocation issue
ENV NODE_OPTIONS="--max-old-space-size=4096"
@ -32,7 +32,7 @@ COPY ./server/ ./server/
RUN npm install -g @nestjs/cli
RUN npm --prefix server run build
FROM node:18.3.0-buster
FROM node:18.18.2-buster
# copy postgrest executable
COPY --from=postgrest/postgrest:v10.1.1.20221215 /bin/postgrest /bin

View file

@ -1,9 +1,9 @@
FROM node:18.3.0-buster AS builder
FROM node:18.18.2-buster AS builder
# Fix for JS heap limit allocation issue
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN npm i -g npm@8.11.0
RUN npm i -g npm@9.8.1
RUN mkdir -p /app
WORKDIR /app
@ -42,12 +42,12 @@ RUN apt-get update -yq \
&& apt-get clean -y
RUN curl -O https://nodejs.org/dist/v18.3.0/node-v18.3.0-linux-x64.tar.xz \
&& tar -xf node-v18.3.0-linux-x64.tar.xz \
&& mv node-v18.3.0-linux-x64 /usr/local/lib/nodejs \
RUN curl -O https://nodejs.org/dist/v18.18.2/node-v18.18.2-linux-x64.tar.xz \
&& tar -xf node-v18.18.2-linux-x64.tar.xz \
&& mv node-v18.18.2-linux-x64 /usr/local/lib/nodejs \
&& echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \
&& /bin/bash -c "source /etc/profile.d/nodejs.sh" \
&& rm node-v18.3.0-linux-x64.tar.xz
&& rm node-v18.18.2-linux-x64.tar.xz
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
ENV NODE_ENV=production

View file

@ -1,9 +1,9 @@
FROM node:18.3.0-buster as builder
FROM node:18.18.2-buster as builder
# Fix for JS heap limit allocation issue
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN npm i -g npm@8.11.0
RUN npm i -g npm@9.8.1
RUN npm install -g @nestjs/cli
RUN mkdir -p /app
@ -32,12 +32,12 @@ RUN apt-get update -yq \
&& apt-get install -yq build-essential \
&& apt-get clean -y
RUN curl -O https://nodejs.org/dist/v18.3.0/node-v18.3.0-linux-x64.tar.xz \
&& tar -xf node-v18.3.0-linux-x64.tar.xz \
&& mv node-v18.3.0-linux-x64 /usr/local/lib/nodejs \
RUN curl -O https://nodejs.org/dist/v18.18.2/node-v18.18.2-linux-x64.tar.xz \
&& tar -xf node-v18.18.2-linux-x64.tar.xz \
&& mv node-v18.18.2-linux-x64 /usr/local/lib/nodejs \
&& echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \
&& /bin/bash -c "source /etc/profile.d/nodejs.sh" \
&& rm node-v18.3.0-linux-x64.tar.xz
&& rm node-v18.18.2-linux-x64.tar.xz
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
ENV NODE_ENV=production

View file

@ -1,5 +1,5 @@
# pull official base image
FROM node:18.3.0-buster
FROM node:18.18.2-buster
RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 wget
# Install Instantclient Basic Light Oracle and Dependencies
@ -19,7 +19,7 @@ WORKDIR /
ENV NODE_ENV=development
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN npm i -g npm@8.11.0
RUN npm i -g npm@9.8.1
RUN mkdir -p /app
WORKDIR /app

View file

@ -0,0 +1,136 @@
---
id: platform-overview
title: Platform Overview
slug: /
---
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
<!-- ## What is ToolJet?
ToolJet is a fast, secure, and user-friendly development platform to build custom internal tools. ToolJet streamlines the development process with seamless integrations, robust security, and a comprehensive suite of app-building tools. -->
## What is ToolJet?
ToolJet is a low-code platform that enables developers to rapidly build and deploy custom internal tools. It has a drag-and-drop app builder with 45 pre-built components, so developers can create complex applications in minutes. ToolJet also connects to most popular data sources and APIs out of the box, and it has a group-based permission system for easy user access management. ToolJet also comes with a lot of other features, but for now, lets build a basic ToolJet app.
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## How ToolJet Works:
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px', borderRadius:'5px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)' }} className="screenshot-full" src="/img/platform-overview/platform-overview-v2.jpg" alt="Platform Overview" />
</div>
**With ToolJet, you can streamline app development with 4 core steps:** <br/>
**Connect Data Sources**: Leverage ToolJet's robust integration features to connect with any data source. The platform supports seamless data integration across over 50 different applications, databases, and APIs.
**Design Stunning Interfaces**: Drag and drop UI components like Tables, Charts, Forms, and more build custom applications in minutes. Integrate these components with data sources and incorporate business logic through JavaScript or Python.
**Automate Complex Workflows**: Develop multi-step workflows in ToolJet to automate business processes. In addition to building and automating workflows, ToolJet allows for easy integration of these workflows within your applications.
**Secure and Manage**: Secure your internal tools with detailed permissions settings and audit logs. Maintain quality and consistency with version control, and keep track of performance with comprehensive observability tools.
</div>
<div style={{paddingBottom:'24px'}}>
Below is a detailed overview of ToolJet's key functionalities, demonstrating how ToolJet helps teams to build more with less effort and greater efficiency.
### Visual App Builder
Enables the creation of visually appealing front-ends with a drag-and-drop interface and pre-built components.
<!-- It simplifies the app-development process, making it accessible even for non-technical users. -->
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/platform-overview/app-builder.png" alt="App-Builder" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Integrations
Offers seamless integration with a wide range of data sources, including over 50 applications, databases, and APIs.
<!-- This feature facilitates easy data connectivity and aggregation from various systems. -->
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/platform-overview/integrations.png" alt="Integrations" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### ToolJet Database
A robust, scalable database solution built atop PostgreSQL. It allows for no-code database management, enabling users to build, manage, and scale databases effortlessly.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/platform-overview/tooljet-db.png" alt="ToolJet Database" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Workflow Automation
Simplifies the automation of complex manual business processes, reducing the engineering effort required.
<!-- This feature is particularly useful for streamlining enterprise workflows and improving operational efficiency. -->
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/platform-overview/workflows.png" alt="Workflows" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Enterprise-Grade Security
Designed with advanced security features and a scalable infrastructure to meet the needs of enterprise teams.
<!-- This ensures the protection of sensitive data and the reliability of the platform in handling large-scale applications. -->
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', borderRadius:'5px'}} className="screenshot-full" src="/img/platform-overview/security.png" alt="Security" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### SSO Support
Single Sign-On (SSO) capabilities, supporting a variety of providers including Okta, Google, Azure AD, and OpenID Connect.
<!-- This enhances user authentication and bolsters overall security. -->
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', borderRadius:'5px'}} className="screenshot-full" src="/img/platform-overview/sso.png" alt="SSO Support" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Multiple Environments
Creation and management of multiple environments for efficient application lifecycle management, allowing different stages like development, testing, and production to be handled seamlessly.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', borderRadius:'5px'}} className="screenshot-full" src="/img/platform-overview/multi-environment.png" alt="SSO Support" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Multiplayer Editing
Multiple users can collaboratively work on app development in real-time. Simultaneous edits and contributions from different team members streamlines the development process and fosters a more dynamic and interactive workspace
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom: '15px' }} className="screenshot-full" src="/img/platform-overview/multiplayer.png" alt="Multiplayer Editing" />
</div>
</div>
Whether you're a seasoned developer or a business professional, ToolJet stands out as a comprehensive solution to fast-track your internal tool development process.

View file

@ -0,0 +1,236 @@
---
id: quickstart-guide
title: Quickstart Guide
---
<!-- <div style={{paddingTop:'24px', paddingBottom:'24px'}}> -->
This Quickstart Guide will show you how to create an employee directory application in minutes using ToolJet. This app will let you track and update employee information with a beautiful user interface. Here are the step-by-step instructions:
**[1. Create Your First Application](#1-create-your-first-application)** <br/>
**[2. Create Employee Database](#2-create-employee-database)** <br/>
**[3. Integrate Data](#3-integrate-data)** <br/>
**[4. List Employees](#4-list-employees)** <br/>
**[5. Add New Employee](#5-add-new-employee)** <br/>
**[6. Preview, Release And Share](#6-preview-release-and-share)** <br/>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
<!-- </div> -->
### 1. Create Your First Application
Once you have created an account with ToolJet, go to the dashboard and click on the Create new app button. Name your application as "Employee Directory". You are ready to design your application now.
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/b4059c71-812d-407b-9d4a-fc598eac6260-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder='0'
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
Click and drag a **[Table](/docs/widgets/table)** component to the canvas.
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/ee10d678-63fb-4fd5-a217-835ddd0898e9-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
Optionally, you can style the Table by **[adjusting its styling properties](/docs/tooljet-concepts/what-are-components#customizing-components)** and create a header by placing **[Text](/docs/widgets/text)** components over a **[Container](/docs/widgets/container)** component and styling them.
<div style={{textAlign: 'center'}}>
<img style={{marginBottom:'15px', borderRadius: '6px' }} className="screenshot-full" src="/img/quickstart-guide/header-design-v2.png" alt="Database Preview" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### 2. Create Employee Database
Now, create a new table in **[ToolJets Database](/docs/tooljet-database/)** to store employee records. Name the table employees and add the following columns: `firstname`, `lastname`, `email`, `phone`, `department`, `position`, `joining`, and `status`. Also, add a few employee records in the table.
<div style={{textAlign: 'center'}}>
<img style={{marginBottom:'15px'}} className="screenshot-full" src="/img/quickstart-guide/create-database-v2.png" alt="Database Preview" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### 3. Integrate Data
To display employees in the application, we first need to fetch data from the database using a query:
- Click on the Add button in the **[Query Panel](/docs/app-builder/query-panel/)**, select ToolJet Database
- Rename the query to `getEmployees`
- Choose `employees` as Table name, List rows as Operations
- Toggle Run this query on application load? to automatically run the query when the app starts
- Click on Run to fetch data
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/de162474-7861-4275-bc8a-da275517908c-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
Click on the Preview button to see a preview of the fetched data.
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/b01e837b-b1b0-468e-a4e3-4b8064ba2e56-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### 4. List Employees
Now, we need to bind the data returned by the `getEmployees` query above with the Table created in Step 1. Click on the Table component to open its configuration panel on the right. Under the `Data` property, paste the below code:
```js
{{queries.getEmployees.data}}
```
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/f780f25f-0832-4a06-86f2-46864b891db1-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
Now the Table component is filled with the data returned by the `getEmployees` query.
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### 5. Add New Employee
Next step is to create a way to add data for new employees.
- Click on Add in the query panel, select ToolJet Database
- Select `employees` as Table name, Create row as Operations
- Rename the query to `addEmployee`
- Click Add Column to add required columns
- Enter code below for **email** and **firstname** column keys:
```js
{{components.table1.newRows[0].email}}
{{components.table1.newRows[0].firstname}}
...
```
Frame all the remaining keys in the same format.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px', borderRadius: '6px' }} className="screenshot-full" src="/img/quickstart-guide/add-employee-query-v2.png" alt="Add Employee Query" />
</div>
Let's continue working on this query. The data needs to reload once this query runs since we want the Table component to be populated with the updated data. Follow the below steps to run the `getEmployees` query after the `addEmployee` query is completed.
- Scroll down and click on New event handler
- Select Query Success as Event and Run Query as Action
- Select `getEmployees` as Query
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px', borderRadius: '6px'}} className="screenshot-full" src="/img/quickstart-guide/reload-data-v2.png" alt="Reload Table Data" />
</div>
We are now ready with a query that will allow us to add new employee data. Let's link this query to a button.
In the bottom-right corner of the Table component, there is a `+`/Add new row button. Follow the below steps to run the `addEmployee` query on click of the `+`/Add new row button:
- Click on the Table component, go to Events in configuration panel and add a New event handler
- Choose Add new rows as Event, Run Query as Action
- Select `addEmployee` as the Query
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/e53c2517-41f1-4ee0-a5c0-59f5c3622c4a-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
Now if you click on the `+`/Add new row button, enter the employee data and click on Save. The `addEmployee` query will run and the data will be written to the `employees` table in the ToolJet Database.
<div style={{marginBottom:'15px', height:'397px', }}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/a33b3f9d-a33d-49c3-a031-db09b0202cfd-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### 6. Preview, Release And Share
The preview, release and share buttons are on the top-right of the App-Builder.
- Click on the Preview on the top-right of app builder to review how your application is coming along while development.
- Once the development is done and you are ready to use the application, click on Release button to deploy the app.
- Finally, share your application with your end users using Share button
<div style={{textAlign: 'center'}}>
<img style={{marginBottom:'15px'}} className="screenshot-full" src="/img/quickstart-guide/preview-share-v2.png" alt="Preview And Share" />
</div>
Congratulations! You've successfully built an employee directory application and, in the process, covered the fundamentals of ToolJet.
To learn more about how ToolJet works, explore the subjects covered in **[ToolJet Concepts](/docs/tooljet-concepts/what-are-components)**.
</div>

252
docs/docs/gitsync.md Normal file
View file

@ -0,0 +1,252 @@
---
id: gitsync
title: GitSync
---
GitSync feature allows users to synchronize the applications on their workspace with a git repository. GitSync feature simplifies the process of managing and version controlling your applications on ToolJet.
## Overview
ToolJet applications can be synchronized with a Git repository, offering the flexibility to tailor your application development and deployment processes across various environments while aligning with best practices for the application development lifecycle.
### Key Use-Cases:
#### Backup of Apps
GitSync provides a straightforward solution for creating backups of your applications. By pushing changes to a Git repository, users can ensure a secure and versioned history of their application. This serves as a reliable backup mechanism, safeguarding against accidental application/version deletion or corruption.
#### Environment Migration
Facilitating the movement of applications across different ToolJet deployments (e.g., from development to staging to production), GitSync acts as a pivotal tool for environment migration. Users can effortlessly transfer their applications across environments by pushing changes to a Git repository.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/envmigration.png" alt="GitSync" />
</div>
## Setting up GitSyncing with GitHub
:::caution
- ToolJet support git repo managers like GitHub, GitLab, Bitbucket, AWS CodeCommit, and Azure Repos.
- Only Admins have the permission to configure the GitSync feature on workspace level.
:::
### Step 1: Create a new repository on GitHub
Create a new repository on GitHub. The repository can be public or private. You can also use an existing repository. Make sure that the repository is empty.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/github1.png" alt="GitHub" />
</div>
### Step 2: Obtain the repository URL
Obtain the **SSH URL** of the repository. When a repository is created, GitHub shows a screen with the repository URL. If the repository is already created, you can obtain the URL by clicking on the **Clone or download** button.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/github2.png" alt="GitHub" />
</div>
### Step 3: Configure the GitSync feature on ToolJet
Go to the **Workspace settings**, and click on the **Configure git** tab.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/gitsync.png" alt="GitSync" />
</div>
<br/>
Enter the **SSH URL** of the repository (obtained in Step 2) in the **Git repository URL** field. Click on the **Generate SSH key** button, and copy the SSH key that is generated. The SSH key is used to authenticate ToolJet with the repository.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/git2.png" alt="GitSync" />
</div>
### Step 4: Deploy the SSH key to GitHub repository
Go to the **Settings** tab of the GitHub repository that you created in Step 1, and click on the **Deploy keys** tab. Click on the **Add deploy key** button.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/github3.png" alt="GitHub" />
</div>
Enter a title for the SSH key in the **Title** field. Paste the SSH key that you copied in Step 3 in the **Key** field. Make sure that the **Allow write access** checkbox is checked, especially when configuring the GitSync feature to [push changes to Git](#pushing-changes-to-git-repo). However, it is not mandatory to check this option when setting up the GitSync feature for [pulling changes from Git](#pulling-changes-from-git-repo). Finally, click on the **Add key** button.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/github4.png" alt="GitHub" />
</div>
### Step 5: Finish the GitSync configuration on ToolJet
Go back to the **Configure git** tab on ToolJet, and click on the **Finalize setup** button. If the SSH key is configured correctly, you will see a success message.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/git5.png" alt="GitSync" />
</div>
## Enable/Disable GitSync
To enable or disable the GitSync feature, go to the **Configure git** tab on the **Workspace settings** page, and toggle on/off the **Connect** switch. This is only available if the GitSync feature is configured.
**When enabled**
On clicking the GitSync button, the users will be able to commit changes to the git repository.
**When disabled**
1. For non-admin users: The users will not be able to commit changes to the git repository. They will see a dialogue box that the GitSync feature is not configured and they need to contact the admin to configure it.
2. For admin users: The users will see a dialogue box with a link to configure the GitSync feature.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/connect.png" alt="GitSync" />
</div>
## Delete GitSync configuration
To delete the GitSync configuration, go to the **Configure git** tab on the **Workspace settings** page, and click on the **Delete configuration** button. This will delete the SSH key from the ToolJet configuration and the GitSync feature will be disabled.
**Note:**
- Deleting the GitSync configuration will not delete the apps from the git repository. The apps will still be available in the git repository in the same state as they were before the GitSync configuration was deleted.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/deleteconfig.png" alt="GitSync" />
</div>
## Git repo
Once the initial commit is made, you can see the app files in the git repository. The repository will have the individual app folders and a **.meta** folder. The app folders will be named as the app name and will have the respective **JSON** file of the application. The **.meta** folder will have the `meta.json` file that contains the meta information of each application synced to git repo.
The **meta.json** file holds information about apps such as the **App name**, **last commit message**, **last commit user**, **last commit date**, **version name**, and **version id**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/gitcommit.png" alt="GitSync" />
</div>
## Pushing changes to git repo
Once the GitSync feature is configured, you can start pushing changes to the git repository.
### App creation
When you create a new app, you will see an option to select the `Commit changes`. If you select the `commit changes` option, the changes will be committed to the git repository.
:::info
If the app name is same as the name of the existing app in the git repo, it will overwrite the existing app in the git repo.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/commitchanges.png" alt="GitSync" />
</div>
<br/>
Selecting the `Commit changes` option will create a new commit in the git repository. The commit message will be `App creation` and the author will be the user who created the app.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/firstcommit.png" alt="GitSync" />
</div>
### App rename
Whenever an app is renamed, the changes will be automatically committed to the git repository. The commit message will be `App is renamed` and the author will be the user who renamed the app.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/rename.png" alt="GitSync" />
</div>
### App updates
Whenever a user makes a change in an app, they can make a commit to the git repository by clicking on the **GitSync** button on the topbar. On clicking the **GitSync** button, a modal will open with the option to enter the commit message. The user can enter the commit message and click on the **Commit changes** button to commit the changes to the git repository. Along with the commit message, the user can also see the connnected **Git repo URL** and the **last commit details**.
**Last commit details** helps the user to know the last commit message, author, date, and time. This helps the user to know the last commit details and make the commit message accordingly.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/modalgit.png" alt="GitSync" />
</div>
<br/>
Once the changes are committed, the user can see the commit message, author, and date in the git repository.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/commitgitsync.png" alt="GitSync" />
</div>
### App deletion
Whenever a user deleted an app from the workspace, the app will not be deleted from the git repository. The app will be available in the git repository in the same state as it was before the app was deleted.
### App version update
Whenever a user creates a new app version and creates a commit to git repository, the **JSON** file in the app folder will be replaced with the new version of the app that was created. The **meta.json** file in the **.meta** folder will also be updated with the new version id and version name.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/replace.png" alt="GitSync" />
</div>
## Pulling changes from git repo
You can configure the GitSync feature on another workspace to pull the changes from the git repository. To configure the GitSync feature on another workspace, follow the steps mentioned in the [Setting up GitSyncing with GitHub](#setting-up-git-syncing-with-github) section.
Once the GitSync feature is configured, go to the ToolJet dashboard and click on the three dots on the right side of the **Create new app** button. Click on the **Import from git repository** option.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/importgit.png" alt="GitSync" />
</div>
<br/>
On clicking the **Import from git repository** option, a modal will open with the dropdown to select the app to be imported from the git repository. Once the app is selected, the app name and the last commit will be displayed. Click on the **Import app** button to import the app from the git repository.
:::caution
- The app imported from the git repository cannot be edited.
- The app imported from the Git repository should have a unique name. If the app's name is the same as that of an existing app in the workspace, the user will need to either rename the existing app or delete it to successfully import another app with the same name.
- Workspace constants are not synced with the git repository. After pulling the app, if the app throws an error, the user will need to manually add the workspace constants.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/importmodal.png" alt="GitSync" />
</div>
### Checking for updates
You can check for updates in the git repository by clicking on the **GitSync** button on the topbar. On clicking the **GitSync** button, a modal will open with the option to **Check for updates**. Click on the **Check for updates** button to check for updates in the git repository. If there are any updates, you will see the details of the updates such as commit message, author, and the date in the modal. Click on the **Pull changes** button to pull the changes from the git repository.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/gitsync/updatecheck.png" alt="GitSync" />
</div>

View file

@ -0,0 +1,36 @@
---
id: how-to-access-values
title: How to Access Values?
---
In ToolJet, double curly braces `{{}}` can be used to retrieve data returned by queries, access values related to components and pass custom code. You can see the list of all accessible values in the **[Inspector](/docs/how-to/use-inspector/)** tab in the left sidebar.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px'}} className="screenshot-full" src="/img/tooljet-concepts/writing-custom-code/inspector.png" alt="Check Available Values Using Inspector" />
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Accessing Values
The **queries** keyword can be used to access data returned by queries. For example:`{{queries.getSalesData.data}}`
Similarly, the **components** keyword can be used to access data in the components and other component-related variables. For example: `{{components.table1.selectedRow.id}}`.
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
### Writing Custom Code
You can write custom JavaScript code to set colors, enable or disable toggles and more by passing in JavaScript code inside double curly braces. To change Background Color of a button based on the light or dark theme using **fx** (next to properties in configuration panel), you can use a code that returns a string value of hex code. <br/>
For example, `{{globals.theme.name == "light" ? "#375FCF" : "#FFFFFF"}}`
Similary, to enable or disable a button based on user input using **fx**, you can write a JavaScript code that returns true or false. <br/>
For example, `{{components.form1.data.textinput1 == "" ? true : false}}`.
</div>

View file

@ -0,0 +1,25 @@
---
id: integrating-data
title: Integrating Data
---
Queries allows you to interact with various data sources, such as databases, APIs, and third-party services. They act as the bridge between your application's components and the data you wish to display, manipulate, or store.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/integrating-data/query-example.png" alt="Styles Tab" />
</div>
These queries are constructed in the Query Panel in the App-Builder, a dedicated section within the ToolJet App-Builder, where you can write low-code or custom SQL statements, API requests, or other data retrieval methods.
## Configuring Queries
You can configure queries to run automatically when an application loads, or trigger them based on specific events or user actions. For example, you could set up a query to run when a user clicks a button, fills out a form, or selects an item from a dropdown menu. This enables you to create dynamic, interactive applications.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/integrating-data/trigger-query.png" alt="Trigger Query" />
</div>
## Integration of Queries and Components
Queries are deeply integrated with ToolJet's components. Once a query fetches data, you can easily bind that data to various components in your application using ToolJet's templating syntax. Similarly, you can use queries to create, write or update data and trigger them on button clicks and other events.

View file

@ -0,0 +1,39 @@
---
id: permissions
title: Securing Applications Through Permissions
---
ToolJet employs a Role-Based Access Control (RBAC) system to manage security and access to its resources, which include apps, folders, and workspace variables.
In this system, Admins have the authority to invite Users to their workspaces and assign them to specific Groups. Each Group is associated with a set of Permissions that dictate what level of access its members have to various resources.
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Groups
By default, there are two groups: **All Users**, which contains all workspace members, and **Admins**, which grants full access to all ToolJet resources. Custom groups like Support or Engineering can also be created to fine-tune access controls.
<div style={{marginBottom:'15px', height:'397px'}}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/a2f9229b-255a-44d5-a25a-35ad72de7125-flo.html"
style={{width: '100%', height: '100%', border: '0'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Setting Permissions Based on Groups and Permissions
To secure your applications in ToolJet, you can leverage Groups and Permissions. For instance, you could create a custom group named Finance Team and assign it permissions to only access financial apps and variables within the workspace. When you invite new users, you can directly assign them to this group, ensuring they only have access to the resources they need to perform their tasks. You can also make the app public and make it accessible to users without the need to log in.
By thoughtfully configuring these settings, you can create a secure environment tailored to your organization's needs.
</div>

View file

@ -0,0 +1,30 @@
---
id: styling-components
title: Styling Components
---
<div style={{marginLeft: "40px", marginRight: "40px"}}>
Styling components in ToolJet is a straightforward yet powerful way to enhance the visual appeal and usability of your application. Once you've dragged and dropped a component onto the canvas in the App-Builder, you can access its styling options through the configuration panel on the right side.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/styling-components/styles-tab.png" alt="Styles Tab" />
</div>
The Styles tab on this panel allows you to modify various visual properties such as colors, fonts, borders, and dimensions. You can also apply conditional styling based on data or user interactions, enabling you to create a more dynamic and responsive user interface.
## Intuitive Styling Options
The styling options in ToolJet are designed to be intuitive, eliminating the need for extensive CSS or design experience. You can easily change the background color of a button, adjust the font size of a text field, or add padding and margins to layout components. These styling changes are immediately reflected on the canvas, providing real-time feedback as you build your application.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/styling-components/styling-options.gif" alt="Styling Options" />
</div>
## Custom CSS
Beyond basic styling, ToolJet also offers advanced customization capabilities. For users who are comfortable with CSS, there's the option to add custom classes. This opens up endless possibilities for fine-tuning the appearance and behavior of your application. Whether you're aiming for a specific brand aesthetic or need to meet particular accessibility standards, ToolJet's styling options give you the flexibility to create an application that not only functions well but also looks great.
</div>

View file

@ -0,0 +1,42 @@
---
id: using-fx
title: Using the FX Functionality
---
Clicking on the **fx** symbol in ToolJet opens up a code editor that allows you to write custom JavaScript expressions. You can find **fx** in the configuration panel on the right, next to various properties and settings of a component.
With **fx**, you can perform calculations or set conditional logic to dynamically configure the components without leaving the ToolJet interface. It's an invaluable tool for adding complexity and interactivity to your applications.
## Toggle Button
When using **fx** buttons associated with toggle buttons, the expected output of the code you enter should be a boolean value - `true` or `false`. For example, the below code will check whether the entered age entered in the number input field of the form is above 18, the button component will be enabled or disabled based on it.
```js
{{components.form1.data.numberinput1.value>18? false : true}}
```
<div style={{marginBottom:'15px', height:'492px'}}>
<iframe
className="screenshot-full"
src="https://www.floik.com/embed/e4f537b5-7b36-4760-9a52-caefc659a90b/4931d426-a33c-47c4-a30f-b34283b482ec-flo.html"
style={{width: '100%', height: '100%'}}
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen>
</iframe>
</div>
For other cases, the expected value is a string. For example, If you are setting `Text color`, `Background Color`, `Loader Color`, etc. You need to pass in a JavaScript code that returns a hex code as a string.
## Access all Variables, Queries, and Components
The expressions you write in the code editor available after clicking on **fx** lets you access all the variables, queries, and components within your application. This allows you to create intricate relationships between different parts of your app, making it more responsive and user-friendly.

View file

@ -0,0 +1,44 @@
---
id: what-are-components
title: What Are Components?
---
Components in ToolJet serve as the building blocks for creating applications. They are pre-designed elements that you can drag and drop onto the canvas in the App-Builder. ToolJet comes with 45+ built-in components.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-components/drag-drop-components.gif" alt="Drag And Drop Components" />
</div>
These components range from basic UI elements like buttons, text fields, and tables, to more complex elements like kanban, charts, and maps. By using components, you can quickly assemble a functional and visually appealing application without having to write code from scratch.
### Customizing Components
Components are highly customizable and interactive. Once you place a component on the canvas, you can easily modify its properties, styles, and behaviors through the configuration panel on the right side of the App-Builder. This allows you to make your application dynamic and responsive.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-components/component-config.gif" alt="Component Configuration" />
</div>
### Using Components With Data
In ToolJet, components can be easily connected to various data sources like databases, APIs, and third-party services through **[queries](what-are-queries)**. Once the data is fetched, you can bind it to components like tables, charts, and more.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-components/adding-data-to-component.png" alt="Adding Data To Component" />
</div>
## Custom Components
ToolJet allows for the creation of custom components using React. This feature is invaluable for developers who require functionalities beyond the 45+ built-in components that ToolJet offers. To create a custom component, you can drag and drop a **[Custom Component](/docs/widgets/custom-component/)** on the canvas and configure its data and code.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px', marginBottom:'15px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-components/custom-components.png" alt="Custom Components" />
</div>
By incorporating custom React components, you can significantly extend the capabilities of your ToolJet applications, allowing for a more tailored and unique user experience.
To explore the full list of components in ToolJet, go through the **[Component Library](/docs/widgets/bounded-box)**.

View file

@ -0,0 +1,37 @@
---
id: what-are-datasources
title: What Are Data Sources?
---
Data sources are pivotal as they enable us to fetch and send data to and from different sources including databases, external APIs, or services. Once a data source is configured, it can be shared across all apps within a workspace.
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Types and Management of Data Sources
Apart from its built-in database, ToolJet supports a range of external data sources which can be broadly categorized into databases, external APIs, and services. To manage these data sources, ToolJet provides a data source manager that can be opened by clicking on the **Data Sources** button located on the left-sidebar of the App-Builder.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-datasources/data-source-manager.png" alt="Data Source Manager" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Adding A Data Source
Adding a new data source is as easy as filling out a form; users can click on the Data Sources button in the left-sidebar, navigate to the required data source, click on the corresponding **Add** button and enter the credentials.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-datasources/configure-data-source.gif" alt="Configuring Data Source" />
</div>
</div>
To see a full list of compatible data sources and their set up details, checkout the **[Datasource Catalog](/docs/data-sources/overview)**.

View file

@ -0,0 +1,32 @@
---
id: what-are-events
title: What Are Events?
---
Events are used to run queries, show alerts and other functionalities based on triggers such as button clicks or query completion. Events can be chained together to run a series of logical operations. For example, the completion of one query could trigger another event that runs a second query, and so on. This way, a single user interaction, like clicking a button, could set off a chain of events.
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Triggering Events
Suppose you have a query that refreshes data when a user clicks on a button, and you also want to display a pop-up alert upon successful data refresh. In ToolJet, you can configure an event to trigger a query upon clicking the button, followed by another event to display a pop-up alert confirming the successful data refresh after the query execution is completed.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-events/events-configuration.png" alt="Event Configuration" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Setting Up Event Handlers
Setting up event handlers to manage such triggers and responses is a straightforward process in ToolJet. For instance, to set up an event that triggers on the click of a button, you simply navigate to the button component's configuration, click on **New Event Handler**, and define the Event and the Action to be taken. The actions could range from running a query, showing an alert, or even switching to a different page.
</div>

View file

@ -0,0 +1,42 @@
---
id: what-are-queries
title: What Are Queries?
---
**Queries** act as a bridge between the application and data sources. Queries help interact with data sources like databases or APIs. They fetch or update data based on events like button clicks, making apps dynamic.
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Creation and Management
**Query Panel** is the hub for creating and managing queries to interact with connected data sources. Located at the bottom of the app-builder, it allows users to perform API requests, database queries, and data manipulations using JavaScript and Python. The Query Panel is divided into two main sections: on the left, the **Query Manager** allows for the listing and management of queries; on the right, the **Query Editor** provides the functionality to construct queries either through a low-code interface or by manually entering the query text.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-queries/query-panel.png" alt="Query Panel" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Execution and Interaction
Queries run when triggered by app events, such as clicking a button. They can fetch new data or change existing data, and the results can be displayed in the app using tables or charts. This makes data interaction in your app straightforward and effective.
<div style={{textAlign: 'center'}}>
<img style={{padding: '10px'}} className="screenshot-full" src="/img/tooljet-concepts/what-are-queries/trigger-query.png" alt="Trigger Query Config" />
</div>
</div>
Learn more about queries in this **[detailed guide](/docs/app-builder/query-panel/)** for Query Panel.

View file

@ -10,7 +10,26 @@
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
docs: [
'getting-started',
{
'type': 'category',
'label': 'Getting Started',
'items': [
'getting-started/platform-overview',
'getting-started/quickstart-guide',
],
},
{
'type': 'category',
'label': 'ToolJet Concepts',
'items': [
'tooljet-concepts/what-are-components',
'tooljet-concepts/what-are-datasources',
'tooljet-concepts/what-are-queries',
'tooljet-concepts/what-are-events',
'tooljet-concepts/how-to-access-values',
'tooljet-concepts/permissions',
],
},
{
'type': 'category',
'label': 'Setup',
@ -212,6 +231,7 @@ const sidebars = {
'tutorial/keyboard-shortcuts',
],
},
{
'type': 'category',
'label': 'Workflows',
@ -302,6 +322,7 @@ const sidebars = {
'tutorial/versioning-and-release',
],
},
'gitsync',
{
'type': 'category',
'label': 'Marketplace',

View file

@ -18,7 +18,7 @@
--ifm-color-primary-lighter: rgb(102, 212, 189);
--ifm-color-primary-lightest: rgb(146, 224, 208);
--ifm-code-font-size: 95%;
--ifm-menu-color-active: #4d72fa;
--ifm-menu-color-active: #3E63DD;
--ifm-font-family-base: 'IBM Plex Sans';
--tblr-blue: #206bc4;
--tblr-azure: #4299e1;
@ -43,6 +43,40 @@
--tblr-font-sans-serif: "Inter", -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
--tblr-font-monospace: null, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
--tblr-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--doc-sidebar-width: 250px !important;
--ifm-link-color: #3e63dd;
--ifm-menu-color-background-active: #f0f4ff;
--ifm-link-menu-color-background-hover: #f0f4ff;
}
/* .markdown h1 {
margin-left: 10%;
margin-right: 10%;
} */
.menu__link--sublist-caret:after {
background: var(--ifm-menu-link-sublist-icon) 50% / 1rem 1rem;
}
.menu__caret:before {
background: var(--ifm-menu-link-sublist-icon) 50% / 1rem 1rem !important;
}
.badge--primary {
margin-bottom: 1rem;
}
article {
padding-left: 7%;
padding-right: 7%;
}
.menu__caret {
size: small;
}
p {
line-height: 180%;
}
.admonition {

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

BIN
docs/static/img/gitsync/connect.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

BIN
docs/static/img/gitsync/deleteconfig.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

BIN
docs/static/img/gitsync/envmigration.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
docs/static/img/gitsync/firstcommit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

BIN
docs/static/img/gitsync/git2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

BIN
docs/static/img/gitsync/git5.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

BIN
docs/static/img/gitsync/gitcommit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

BIN
docs/static/img/gitsync/github1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

BIN
docs/static/img/gitsync/github2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

BIN
docs/static/img/gitsync/github3.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

BIN
docs/static/img/gitsync/github4.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

BIN
docs/static/img/gitsync/gitsync.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

BIN
docs/static/img/gitsync/importgit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

BIN
docs/static/img/gitsync/importmodal.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

BIN
docs/static/img/gitsync/modalgit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

BIN
docs/static/img/gitsync/rename.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

BIN
docs/static/img/gitsync/replace.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

BIN
docs/static/img/gitsync/updatecheck.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

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