mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-06 06:48:21 +00:00
Merge branch 'appbuilder/sprint-14' into feat/query-permissions
This commit is contained in:
commit
fcfbb46b90
257 changed files with 26197 additions and 52221 deletions
6
.github/workflows/cypress-appbuilder.yml
vendored
6
.github/workflows/cypress-appbuilder.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
node-version: 22.15.1
|
||||
|
||||
- name: Set up Git authentication for private submodules
|
||||
run: |
|
||||
|
|
@ -160,9 +160,9 @@ jobs:
|
|||
name: screenshots-appbuilder-${{ matrix.edition }}
|
||||
path: cypress-tests/cypress/screenshots
|
||||
|
||||
# Cypress-App-builder-Subpath:
|
||||
# Cypress-App-builder-Subpath:
|
||||
# runs-on: ubuntu-22.04
|
||||
# if: contains(github.event.pull_request.labels.*.name, 'run-cypress') ||
|
||||
# if: contains(github.event.pull_request.labels.*.name, 'run-cypress') ||
|
||||
# contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-subpath')
|
||||
|
||||
# steps:
|
||||
|
|
|
|||
6
.github/workflows/cypress-platform.yml
vendored
6
.github/workflows/cypress-platform.yml
vendored
|
|
@ -228,7 +228,7 @@ jobs:
|
|||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
node-version: 22.15.1
|
||||
|
||||
- name: Set up Git authentication for private submodules
|
||||
run: |
|
||||
|
|
@ -364,7 +364,7 @@ jobs:
|
|||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
node-version: 22.15.1
|
||||
|
||||
- name: Set up Git authentication for private submodules
|
||||
run: |
|
||||
|
|
@ -512,7 +512,7 @@ jobs:
|
|||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
node-version: 22.15.1
|
||||
|
||||
- name: Set up Git authentication for private submodules
|
||||
run: |
|
||||
|
|
|
|||
75
.github/workflows/merging-pr.yml
vendored
Normal file
75
.github/workflows/merging-pr.yml
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
name: Merge Submodule PRs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed, labeled]
|
||||
|
||||
jobs:
|
||||
merge-submodules:
|
||||
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Extract Branch Name
|
||||
run: echo "BRANCH_NAME=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Merge PR in ee-server (if exists)
|
||||
run: |
|
||||
PR=$(gh pr list -R ToolJet/ee-server --head "$BRANCH_NAME" --state open --json number -q '.[0].number')
|
||||
if [ -n "$PR" ]; then
|
||||
echo "Found ee-server PR: #$PR"
|
||||
gh pr merge -R ToolJet/ee-server "$PR" --merge --admin
|
||||
else
|
||||
echo "No open ee-server PR for branch $BRANCH_NAME"
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.TOKEN_PR }}
|
||||
|
||||
- name: Merge PR in ee-frontend (if exists)
|
||||
run: |
|
||||
PR=$(gh pr list -R ToolJet/ee-frontend --head "$BRANCH_NAME" --state open --json number -q '.[0].number')
|
||||
if [ -n "$PR" ]; then
|
||||
echo "Found ee-frontend PR: #$PR"
|
||||
gh pr merge -R ToolJet/ee-frontend "$PR" --merge --admin
|
||||
else
|
||||
echo "No open ee-frontend PR for branch $BRANCH_NAME"
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.TOKEN_PR }}
|
||||
|
||||
check-submodule-prs:
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'ready-to-merge'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Extract Branch Name
|
||||
run: echo "BRANCH_NAME=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Check and Comment Linked Submodule PRs
|
||||
run: |
|
||||
echo "🔍 Checking linked submodule PRs for \`$BRANCH_NAME\`:" > comment.md
|
||||
echo "" >> comment.md
|
||||
|
||||
SERVER_URL=$(gh pr list -R ToolJet/ee-server --head "$BRANCH_NAME" --state open --json url -q '.[0].url')
|
||||
FRONTEND_URL=$(gh pr list -R ToolJet/ee-frontend --head "$BRANCH_NAME" --state open --json url -q '.[0].url')
|
||||
|
||||
if [ -n "$SERVER_URL" ]; then
|
||||
echo "✅ ee-server PR - $SERVER_URL" >> comment.md
|
||||
else
|
||||
echo "❌ No open PR in ee-server" >> comment.md
|
||||
fi
|
||||
|
||||
if [ -n "$FRONTEND_URL" ]; then
|
||||
echo "✅ ee-frontend PR - $FRONTEND_URL" >> comment.md
|
||||
else
|
||||
echo "❌ No open PR in ee-frontend" >> comment.md
|
||||
fi
|
||||
|
||||
echo "" >> comment.md
|
||||
echo "📝 **Note**: The submodule PRs will be auto-merged once you merge this base PR-$PR_NUMBER into \`main\`." >> comment.md
|
||||
|
||||
gh pr comment "$PR_NUMBER" --repo ToolJet/ToolJet --body-file comment.md
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.TOKEN_PR }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
3.14.0
|
||||
3.15.1
|
||||
|
|
|
|||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -8,8 +8,8 @@
|
|||
"typescript",
|
||||
"typescriptreact"
|
||||
],
|
||||
"eslint.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.format.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
|
|
|
|||
|
|
@ -76,8 +76,9 @@ module.exports = defineConfig({
|
|||
experimentalRunAllSpecs: true,
|
||||
baseUrl: "http://localhost:8082",
|
||||
specPattern: [
|
||||
"cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/**/*.cy.js",
|
||||
// "cypress/e2e/happyPath/appbuilder/ceTestcases/**/*.cy.js"
|
||||
// "cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/**/*.cy.js",
|
||||
// "cypress/e2e/happyPath/appbuilder/ceTestcases/**/*.cy.js",
|
||||
"cypress/e2e/happyPath/appbuilder/commonTestcases/newSuits/globalSetingsHappyPath.cy.js"
|
||||
],
|
||||
numTestsKeptInMemory: 1,
|
||||
redirectionLimit: 7,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:18.18.2-buster AS builder
|
||||
FROM node:22.15.1 AS builder
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
|
|
@ -50,10 +50,11 @@ ENV TOOLJET_EDITION=ee
|
|||
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||
RUN npm --prefix server install
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g copyfiles
|
||||
RUN npm --prefix server run build
|
||||
|
||||
FROM node:18.18.2-bullseye
|
||||
FROM node:22.15.1
|
||||
|
||||
RUN apt-get update -yq \
|
||||
&& apt-get install curl wget gnupg zip -yq \
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ export const commonWidgetSelector = {
|
|||
buttonCloseEditorSideBar: "[data-cy='inspector-close-icon']",
|
||||
buttonStylesEditorSideBar: "#inspector-tab-styles",
|
||||
WidgetNameInputField: "[data-cy=edit-widget-name]",
|
||||
constantInspectorIcon: '[data-cy="inspector-node-constants"] > .node-key',
|
||||
constantInspectorIcon: '[data-cy="inspector-constants-expand-button"]',
|
||||
inspectorIcon: '[data-cy="left-sidebar-inspect-button"]',
|
||||
tooltipInputField: "[data-cy='tooltip-input-field']",
|
||||
tooltipLabel: "[id=button-tooltip]",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const groupsSelector = {
|
|||
resourceLabel: '[data-cy="resource-label"]',
|
||||
allAppsRadio: '[data-cy="all-apps-radio"]',
|
||||
allAppsLabel: '[data-cy="all-apps-label"]',
|
||||
allAppsHelperText: '[data-cy="all-apps-info-text"]',
|
||||
allAppsHelperText: '[data-cy="this-will-select-all-apps-in-the-workspace-including-any-new-apps-created-info-text"]',
|
||||
customradio: '[data-cy="custom-radio"]',
|
||||
customLabel: '[data-cy="custom-label"]',
|
||||
customHelperText: '[data-cy="custom-info-text"]',
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export const groupsText = {
|
|||
allAppsLabel: 'All apps',
|
||||
allAppsHelperText: 'This will select all apps in the workspace including any new apps created',
|
||||
customLabel: 'Custom',
|
||||
customHelperText: 'Select specific applications you want to add to the group',
|
||||
customHelperText: 'Select specific apps you want to add to the group',
|
||||
updateButtonText: 'Update',
|
||||
addButtonText: 'Add',
|
||||
userRole: 'User role',
|
||||
|
|
|
|||
|
|
@ -5,213 +5,214 @@ import { commonText } from "Texts/common";
|
|||
|
||||
import { exportAppModalText } from "Texts/exportImport";
|
||||
import {
|
||||
clickOnExportButtonAndVerify,
|
||||
exportAllVersionsAndVerify,
|
||||
verifyElementsOfExportModal,
|
||||
clickOnExportButtonAndVerify,
|
||||
exportAllVersionsAndVerify,
|
||||
verifyElementsOfExportModal,
|
||||
} from "Support/utils/exportImport";
|
||||
import { selectAppCardOption, closeModal } from "Support/utils/common";
|
||||
|
||||
describe("App Export", () => {
|
||||
const TEST_DATA = {
|
||||
appFiles: {
|
||||
multiVersion: "cypress/fixtures/templates/three-versions.json",
|
||||
singleVersion: "cypress/fixtures/templates/one_version.json",
|
||||
},
|
||||
};
|
||||
const TEST_DATA = {
|
||||
appFiles: {
|
||||
multiVersion: "cypress/fixtures/templates/three-versions.json",
|
||||
singleVersion: "cypress/fixtures/templates/one_version.json",
|
||||
},
|
||||
};
|
||||
|
||||
let data;
|
||||
let data;
|
||||
|
||||
data = {
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf *");
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.wait(3000);
|
||||
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
workspaceName: fake.firstName,
|
||||
workspaceSlug: fake.firstName.toLowerCase().replace(/\s+/g, "-"),
|
||||
appName: `${fake.companyName}-IE-App`,
|
||||
appReName: `${fake.companyName}-${fake.companyName}-IE-App`,
|
||||
dsName: fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", ""),
|
||||
};
|
||||
cy.exec("mkdir -p ./cypress/downloads/");
|
||||
cy.wait(3000);
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
});
|
||||
|
||||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
});
|
||||
it("Verify the elements of export dialog box", () => {
|
||||
cy.skipWalkthrough();
|
||||
|
||||
it("Verify the elements of export dialog box", () => {
|
||||
cy.skipWalkthrough()
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
cy.get(importSelectors.importOptionInput)
|
||||
.eq(0)
|
||||
.selectFile(TEST_DATA.appFiles.multiVersion, { force: true });
|
||||
cy.wait(2000);
|
||||
cy.clearAndType(commonSelectors.appNameInput, data.appName);
|
||||
cy.get(importSelectors.importAppButton).click();
|
||||
cy.wait(3000);
|
||||
cy.backToApps();
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
cy.get(importSelectors.importOptionInput)
|
||||
.eq(0)
|
||||
.selectFile(TEST_DATA.appFiles.multiVersion, {
|
||||
force: true,
|
||||
});
|
||||
cy.wait(1500);
|
||||
cy.clearAndType(commonSelectors.appNameInput, data.appName);
|
||||
cy.get(importSelectors.importAppButton).click();
|
||||
cy.wait(3000);
|
||||
cy.backToApps();
|
||||
// Select the app card option to export the app
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
);
|
||||
|
||||
// Select the app card option to export the app
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
// Verify the elements of the export modal
|
||||
verifyElementsOfExportModal("v3", ["v2", "v1"], [true, false, false]);
|
||||
|
||||
// Close the modal
|
||||
closeModal(exportAppModalText.modalCloseButton);
|
||||
|
||||
// Ensure the modal title is no longer visible
|
||||
cy.get(
|
||||
commonSelectors.modalTitle(exportAppModalText.selectVersionTitle)
|
||||
).should("not.exist");
|
||||
|
||||
// Re-open the export modal and click the export button
|
||||
cy.wait(2000);
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
);
|
||||
clickOnExportButtonAndVerify(exportAppModalText.exportAll, data.appName);
|
||||
|
||||
cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
const filePath = `./cypress/downloads/${downloadedAppExportFileName}`;
|
||||
|
||||
// Ensure the file name contains the expected app export name
|
||||
expect(downloadedAppExportFileName).to.contain(
|
||||
data.appName.toLowerCase()
|
||||
);
|
||||
|
||||
// Read and validate the exported JSON file
|
||||
cy.readFile(filePath).then((appData) => {
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(data.appName);
|
||||
|
||||
// Validate the schema for the student table in tooljetdb
|
||||
const tooljetDatabase = appData.tooljet_database.find(
|
||||
(db) => db.table_name === "student"
|
||||
);
|
||||
expect(tooljetDatabase).to.exist;
|
||||
expect(tooljetDatabase.schema).to.exist;
|
||||
|
||||
// Validate components and queries
|
||||
const components = appData.app[0].definition.appV2.components;
|
||||
|
||||
const text2Component = components.find(
|
||||
(component) => component.name === "text2"
|
||||
);
|
||||
expect(text2Component).to.exist;
|
||||
expect(text2Component.properties.text.value).to.equal(
|
||||
"{{constants.pageHeader}}"
|
||||
);
|
||||
|
||||
// Verify the elements of the export modal
|
||||
verifyElementsOfExportModal("v3", ["v2", "v1"], [true, false, false]);
|
||||
|
||||
// Close the modal
|
||||
closeModal(exportAppModalText.modalCloseButton);
|
||||
|
||||
// Ensure the modal title is no longer visible
|
||||
cy.get(
|
||||
commonSelectors.modalTitle(exportAppModalText.selectVersionTitle)
|
||||
).should("not.exist");
|
||||
|
||||
// Re-open the export modal and click the export button
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
const textinput1 = components.find(
|
||||
(component) => component.name === "textinput1"
|
||||
);
|
||||
clickOnExportButtonAndVerify(exportAppModalText.exportAll, data.appName);
|
||||
expect(textinput1).to.exist;
|
||||
expect(textinput1.properties.value.value).to.include("queries");
|
||||
|
||||
cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
const filePath = `./cypress/downloads/${downloadedAppExportFileName}`;
|
||||
|
||||
// Ensure the file name contains the expected app export name
|
||||
expect(downloadedAppExportFileName).to.contain(
|
||||
data.appName.toLowerCase()
|
||||
);
|
||||
|
||||
// Read and validate the exported JSON file
|
||||
cy.readFile(filePath).then((appData) => {
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(data.appName);
|
||||
|
||||
// Validate the schema for the student table in tooljetdb
|
||||
const tooljetDatabase = appData.tooljet_database.find(
|
||||
(db) => db.table_name === "student"
|
||||
);
|
||||
expect(tooljetDatabase).to.exist;
|
||||
expect(tooljetDatabase.schema).to.exist;
|
||||
|
||||
// Validate components and queries
|
||||
const components = appData.app[0].definition.appV2.components;
|
||||
|
||||
const text2Component = components.find(
|
||||
(component) => component.name === "text2"
|
||||
);
|
||||
expect(text2Component).to.exist;
|
||||
expect(text2Component.properties.text.value).to.equal(
|
||||
"{{constants.pageHeader}}"
|
||||
);
|
||||
|
||||
const textinput1 = components.find(
|
||||
(component) => component.name === "textinput1"
|
||||
);
|
||||
expect(textinput1).to.exist;
|
||||
expect(textinput1.properties.value.value).to.include("queries");
|
||||
|
||||
const textinput2 = components.find(
|
||||
(component) => component.name === "textinput2"
|
||||
);
|
||||
expect(textinput2).to.exist;
|
||||
expect(textinput2.properties.value.value).to.include("queries");
|
||||
|
||||
const textinput3 = components.find(
|
||||
(component) => component.name === "textinput3"
|
||||
);
|
||||
expect(textinput3).to.exist;
|
||||
expect(textinput3.properties.value.value).to.include("queries");
|
||||
|
||||
// Validate the data queries
|
||||
const dataQueries = appData.app[0].definition.appV2.dataQueries;
|
||||
|
||||
const postgresqlQuery = dataQueries.find(
|
||||
(query) => query.name === "postgresql1"
|
||||
);
|
||||
expect(postgresqlQuery).to.exist;
|
||||
expect(postgresqlQuery.options.query).to.include(
|
||||
"Select * from {{secrets.db_name}}"
|
||||
);
|
||||
|
||||
const restapiQuery = dataQueries.find(
|
||||
(query) => query.name === "restapi1"
|
||||
);
|
||||
expect(restapiQuery).to.exist;
|
||||
expect(restapiQuery.options.url).to.equal(
|
||||
"https://jsonplaceholder.typicode.com/users/1"
|
||||
);
|
||||
|
||||
const tooljetdbQuery = dataQueries.find(
|
||||
(query) => query.name === "tooljetdb1"
|
||||
);
|
||||
expect(tooljetdbQuery).to.exist;
|
||||
expect(tooljetdbQuery.options.operation).to.equal("list_rows");
|
||||
|
||||
// Ensure appVersions exists
|
||||
const appVersions = appData.app[0].definition.appV2.appVersions;
|
||||
expect(appVersions).to.exist;
|
||||
|
||||
// Map and verify app version names
|
||||
const versionNames = appVersions.map((version) => version.name);
|
||||
expect(versionNames).to.include.members(["v1", "v2", "v3"]);
|
||||
});
|
||||
});
|
||||
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf *");
|
||||
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
const textinput2 = components.find(
|
||||
(component) => component.name === "textinput2"
|
||||
);
|
||||
cy.get(`[data-cy="v1-radio-button"]`).check();
|
||||
cy.get(
|
||||
commonSelectors.buttonSelector(exportAppModalText.exportSelectedVersion)
|
||||
).click();
|
||||
expect(textinput2).to.exist;
|
||||
expect(textinput2.properties.value.value).to.include("queries");
|
||||
|
||||
cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
const filePath = `./cypress/downloads/${downloadedAppExportFileName}`;
|
||||
const textinput3 = components.find(
|
||||
(component) => component.name === "textinput3"
|
||||
);
|
||||
expect(textinput3).to.exist;
|
||||
expect(textinput3.properties.value.value).to.include("queries");
|
||||
|
||||
// Ensure the file name contains the expected app export name
|
||||
expect(downloadedAppExportFileName).to.contain(
|
||||
data.appName.toLowerCase()
|
||||
);
|
||||
// Validate the data queries
|
||||
const dataQueries = appData.app[0].definition.appV2.dataQueries;
|
||||
|
||||
// Read and validate the exported JSON file
|
||||
cy.readFile(filePath).then((appData) => {
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(data.appName);
|
||||
});
|
||||
});
|
||||
const postgresqlQuery = dataQueries.find(
|
||||
(query) => query.name === "postgresql1"
|
||||
);
|
||||
expect(postgresqlQuery).to.exist;
|
||||
expect(postgresqlQuery.options.query).to.include(
|
||||
"Select * from {{secrets.db_name}}"
|
||||
);
|
||||
|
||||
const restapiQuery = dataQueries.find(
|
||||
(query) => query.name === "restapi1"
|
||||
);
|
||||
expect(restapiQuery).to.exist;
|
||||
expect(restapiQuery.options.url).to.equal(
|
||||
"https://jsonplaceholder.typicode.com/users/1"
|
||||
);
|
||||
|
||||
const tooljetdbQuery = dataQueries.find(
|
||||
(query) => query.name === "tooljetdb1"
|
||||
);
|
||||
expect(tooljetdbQuery).to.exist;
|
||||
expect(tooljetdbQuery.options.operation).to.equal("list_rows");
|
||||
|
||||
// Ensure appVersions exists
|
||||
const appVersions = appData.app[0].definition.appV2.appVersions;
|
||||
expect(appVersions).to.exist;
|
||||
|
||||
// Map and verify app version names
|
||||
const versionNames = appVersions.map((version) => version.name);
|
||||
expect(versionNames).to.include.members(["v1", "v2", "v3"]);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("Verify 'Export app' functionality of an application inside app editor", () => {
|
||||
data.appName2 = `${fake.companyName}-App`;
|
||||
cy.apiCreateApp(data.appName2);
|
||||
cy.openApp(data.appName2);
|
||||
cy.exec("cd ./cypress/downloads/ && rm -rf *");
|
||||
|
||||
cy.dragAndDropWidget("Text Input", 50, 50);
|
||||
selectAppCardOption(
|
||||
data.appName,
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
);
|
||||
cy.get(`[data-cy="v1-radio-button"]`).check();
|
||||
cy.get(
|
||||
commonSelectors.buttonSelector(exportAppModalText.exportSelectedVersion)
|
||||
).click();
|
||||
|
||||
cy.get('[data-cy="left-sidebar-settings-button"]').click();
|
||||
cy.get('[data-cy="button-user-status-change"]').click();
|
||||
cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
const filePath = `./cypress/downloads/${downloadedAppExportFileName}`;
|
||||
|
||||
verifyElementsOfExportModal("v1");
|
||||
// Ensure the file name contains the expected app export name
|
||||
expect(downloadedAppExportFileName).to.contain(
|
||||
data.appName.toLowerCase()
|
||||
);
|
||||
|
||||
exportAllVersionsAndVerify(data.appName1, "v1");
|
||||
// Read and validate the exported JSON file
|
||||
cy.readFile(filePath).then((appData) => {
|
||||
// Validate the app name
|
||||
const appNameFromFile = appData.app[0].definition.appV2.name;
|
||||
expect(appNameFromFile).to.equal(data.appName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("Verify 'Export app' functionality of an application inside app editor", () => {
|
||||
data.appName2 = `${fake.companyName}-App`;
|
||||
cy.apiCreateApp(data.appName2);
|
||||
cy.openApp(data.appName2);
|
||||
|
||||
cy.dragAndDropWidget("Text Input", 50, 50);
|
||||
|
||||
cy.get('[data-cy="left-sidebar-settings-button"]').click();
|
||||
cy.get('[data-cy="button-user-status-change"]').click();
|
||||
|
||||
verifyElementsOfExportModal("v1");
|
||||
|
||||
exportAllVersionsAndVerify(data.appName1, "v1");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ describe("App Slug", () => {
|
|||
|
||||
// Release and verify URLs
|
||||
releaseApp();
|
||||
verifyURLs(workspaceId, data.slug, false);
|
||||
verifyURLs(workspaceId, data.slug, true);
|
||||
|
||||
// Verify duplicate slug validation
|
||||
cy.visit("/my-workspace");
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
} from "Support/utils/common";
|
||||
import { inviteUserBasedOnRole } from "Support/utils/manageGroups";
|
||||
import { resolveHost } from "Support/utils/apps";
|
||||
import { addSuccessNotification } from "Support/utils/queries";
|
||||
|
||||
const data = {};
|
||||
data.firstName = fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
|
@ -32,7 +33,7 @@ describe("Datasource Manager", () => {
|
|||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.visit(`${workspaceSlug}`);
|
||||
cy.viewport(1200, 1300);
|
||||
cy.viewport(1800, 1800);
|
||||
cy.skipWalkthrough();
|
||||
});
|
||||
|
||||
|
|
@ -199,7 +200,7 @@ describe("Datasource Manager", () => {
|
|||
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
pinInspector();
|
||||
// pinInspector();
|
||||
|
||||
addQuery(
|
||||
"table_preview",
|
||||
|
|
@ -212,9 +213,11 @@ describe("Datasource Manager", () => {
|
|||
"table_preview "
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||
cy.get('[data-cy="query-tab-settings"]').click();
|
||||
addSuccessNotification("table_preview");
|
||||
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||
verifyValueOnInspector("table_preview", "10 items ");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "table_preview");
|
||||
|
||||
cy.get('[data-cy="show-ds-popover-button"]').click();
|
||||
|
||||
cy.get(".p-2 > .tj-base-btn")
|
||||
|
|
@ -247,9 +250,13 @@ describe("Datasource Manager", () => {
|
|||
cy.get("#react-select-4-listbox")
|
||||
.contains(`cypress-${data.dsName2}-postgresql`)
|
||||
.click();
|
||||
|
||||
cy.get('[data-cy="query-tab-settings"]').click();
|
||||
addSuccessNotification("postgresql");
|
||||
cy.waitForAutoSave();
|
||||
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||
verifyValueOnInspector("table_preview", "4 items ");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "postgresql");
|
||||
|
||||
});
|
||||
|
||||
it.skip("Should verify the query creation and scope changing functionality.", () => {
|
||||
|
|
|
|||
|
|
@ -143,11 +143,11 @@ describe("Workspace constants", () => {
|
|||
cy.get(dataSourceSelector.previewTabRawContainer).contains("secrets is not defined");
|
||||
|
||||
//verify global const should be visible, secrets and deleted const are not in Inspector
|
||||
cy.get(commonWidgetSelector.inspectorIcon).click();
|
||||
cy.get(commonWidgetSelector.constantInspectorIcon).click();
|
||||
cy.get('[data-cy="inspector-node-restapiheaderkey"]').should('exist');
|
||||
cy.get('[data-cy="inspector-node-deleteconst"]').should('not.exist');
|
||||
cy.get('[data-cy="inspector-node-sconst"]').should('not.exist');
|
||||
// cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||
// cy.get(commonWidgetSelector.constantInspectorIcon).click();
|
||||
// cy.get('[data-cy="inspector-node-restapiheaderkey"]').should('exist');
|
||||
// cy.get('[data-cy="inspector-node-deleteconst"]').should('not.exist');
|
||||
// cy.get('[data-cy="inspector-node-sconst"]').should('not.exist');
|
||||
|
||||
//Preview app and verify components
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ export const resolveHost = () => {
|
|||
const baseUrl = Cypress.config("baseUrl");
|
||||
|
||||
const urlMapping = {
|
||||
"http://localhost:8082": "http://localhost:8082",
|
||||
"http://localhost:3000": "http://localhost:3000",
|
||||
"http://localhost:3000/apps": "http://localhost:3000/apps",
|
||||
"http://localhost:4001": "http://localhost:3000",
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ 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.18.2
|
||||
nvm install 22.15.1
|
||||
sudo ln -s "$(which node)" /usr/bin/node
|
||||
sudo ln -s "$(which npm)" /usr/bin/npm
|
||||
|
||||
sudo npm i -g npm@10.9.2
|
||||
|
||||
# Setup openresty
|
||||
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
|
||||
echo "deb http://openresty.org/package/ubuntu bionic main" > openresty.list
|
||||
|
|
@ -56,10 +58,10 @@ envsubst "${VARS_TO_SUBSTITUTE}" < /tmp/nginx.conf > /tmp/nginx-substituted.conf
|
|||
sudo cp /tmp/nginx-substituted.conf /usr/local/openresty/nginx/conf/nginx.conf
|
||||
|
||||
# Download and setup postgrest binary
|
||||
curl -OL https://github.com/PostgREST/postgrest/releases/download/v10.1.1/postgrest-v10.1.1-linux-static-x64.tar.xz
|
||||
tar xJf postgrest-v10.1.1-linux-static-x64.tar.xz
|
||||
curl -OL https://github.com/PostgREST/postgrest/releases/download/v12.2.0/postgrest-v12.2.0-linux-static-x64.tar.xz
|
||||
tar xJf postgrest-v12.2.0-linux-static-x64.tar.xz
|
||||
sudo mv ./postgrest /bin/postgrest
|
||||
sudo rm postgrest-v10.1.1-linux-static-x64.tar.xz
|
||||
sudo rm postgrest-v12.2.0-linux-static-x64.tar.xz
|
||||
|
||||
# Setup app and postgrest as systemd service
|
||||
sudo cp /tmp/nest.service /lib/systemd/system/nest.service
|
||||
|
|
@ -74,7 +76,7 @@ mv /tmp/.env ~/app/.env
|
|||
mv /tmp/setup_app ~/app/setup_app
|
||||
sudo chmod +x ~/app/setup_app
|
||||
|
||||
npm install -g npm@9.8.1
|
||||
npm install -g npm@10.9.2
|
||||
|
||||
# Building ToolJet app
|
||||
npm install -g @nestjs/cli
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ 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.18.2
|
||||
nvm install 22.15.1
|
||||
sudo ln -s "$(which node)" /usr/bin/node
|
||||
sudo ln -s "$(which npm)" /usr/bin/npm
|
||||
|
||||
sudo npm i -g npm@9.8.1
|
||||
sudo npm i -g npm@10.9.2
|
||||
|
||||
# Setup openresty
|
||||
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
|
||||
|
|
@ -58,10 +58,10 @@ envsubst "${VARS_TO_SUBSTITUTE}" < /tmp/nginx.conf > /tmp/nginx-substituted.conf
|
|||
sudo cp /tmp/nginx-substituted.conf /usr/local/openresty/nginx/conf/nginx.conf
|
||||
|
||||
# Download and setup postgrest binary
|
||||
curl -OL https://github.com/PostgREST/postgrest/releases/download/v12.0.2/postgrest-v12.0.2-linux-static-x64.tar.xz
|
||||
tar xJf postgrest-v12.0.2-linux-static-x64.tar.xz
|
||||
curl -OL https://github.com/PostgREST/postgrest/releases/download/v12.2.0/postgrest-v12.2.0-linux-static-x64.tar.xz
|
||||
tar xJf postgrest-v12.2.0-linux-static-x64.tar.xz
|
||||
sudo mv ./postgrest /bin/postgrest
|
||||
sudo rm postgrest-v12.0.2-linux-static-x64.tar.xz
|
||||
sudo rm postgrest-v12.2.0-linux-static-x64.tar.xz
|
||||
|
||||
# Add the Redis APT repository
|
||||
sudo add-apt-repository ppa:redislabs/redis -y
|
||||
|
|
@ -92,7 +92,7 @@ mv /tmp/.env ~/app/.env
|
|||
mv /tmp/setup_app ~/app/setup_app
|
||||
sudo chmod +x ~/app/setup_app
|
||||
|
||||
npm install -g npm@9.8.1
|
||||
npm install -g npm@10.9.2
|
||||
|
||||
# Building ToolJet app
|
||||
npm install -g @nestjs/cli
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:18.18.2-buster AS builder
|
||||
FROM node:22.15.1 AS builder
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
|
|
@ -30,9 +30,10 @@ COPY ./server/package.json ./server/package-lock.json ./server/
|
|||
RUN npm --prefix server install
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g copyfiles
|
||||
RUN npm --prefix server run build
|
||||
|
||||
FROM node:18.18.2-bullseye
|
||||
FROM node:22.15.1-bullseye
|
||||
# copy postgrest executable
|
||||
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
FROM node:18.18.2-buster AS builder
|
||||
FROM node:22.15.1 AS builder
|
||||
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
ENV NODE_OPTIONS="--max-old-space-size=8096"
|
||||
|
||||
RUN npm i -g npm@9.8.1
|
||||
RUN npm i -g npm@10.9.2
|
||||
RUN mkdir -p /app
|
||||
|
||||
WORKDIR /app
|
||||
|
|
@ -31,10 +31,11 @@ ENV NODE_ENV=production
|
|||
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||
RUN npm --prefix server install
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g copyfiles
|
||||
RUN npm --prefix server run build
|
||||
|
||||
FROM debian:11
|
||||
FROM debian:12
|
||||
|
||||
RUN apt-get update -yq \
|
||||
&& apt-get install curl gnupg zip -yq \
|
||||
|
|
@ -42,12 +43,12 @@ RUN apt-get update -yq \
|
|||
&& apt-get clean -y
|
||||
|
||||
|
||||
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 \
|
||||
RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \
|
||||
&& tar -xf node-v22.15.1-linux-x64.tar.xz \
|
||||
&& mv node-v22.15.1-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.18.2-linux-x64.tar.xz
|
||||
&& rm node-v22.15.1-linux-x64.tar.xz
|
||||
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:18.18.2-buster AS builder
|
||||
FROM node:22.15.1 AS builder
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
|
|
@ -50,10 +50,11 @@ ENV TOOLJET_EDITION=ee
|
|||
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||
RUN npm --prefix server install
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g copyfiles
|
||||
RUN npm --prefix server run build
|
||||
|
||||
FROM node:18.18.2-bullseye
|
||||
FROM node:22.15.1-bullseye
|
||||
|
||||
RUN apt-get update -yq \
|
||||
&& apt-get install curl gnupg zip -yq \
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
FROM node:18.18.2-buster AS builder
|
||||
FROM node:22.15.1 AS builder
|
||||
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
RUN npm i -g npm@9.8.1
|
||||
RUN npm i -g npm@10.9.2
|
||||
RUN mkdir -p /app
|
||||
# RUN npm cache clean --force
|
||||
RUN npm cache clean --force
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
@ -54,10 +54,11 @@ ENV TOOLJET_EDITION=ee
|
|||
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||
RUN npm --prefix server install
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g copyfiles
|
||||
RUN npm --prefix server run build
|
||||
|
||||
FROM debian:11
|
||||
FROM debian:12
|
||||
|
||||
RUN apt-get update -yq \
|
||||
&& apt-get install curl wget gnupg zip -yq \
|
||||
|
|
@ -113,12 +114,12 @@ exec /bin/postgrest-original "$@" 2>&1 | sed "s/^/[PostgREST] /"\n\
|
|||
chmod +x /bin/postgrest
|
||||
|
||||
|
||||
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 \
|
||||
RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \
|
||||
&& tar -xf node-v22.15.1-linux-x64.tar.xz \
|
||||
&& mv node-v22.15.1-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.18.2-linux-x64.tar.xz
|
||||
&& rm node-v22.15.1-linux-x64.tar.xz
|
||||
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.14.0
|
||||
3.15.1
|
||||
|
|
|
|||
BIN
frontend/assets/images/GitSSH.png
Normal file
BIN
frontend/assets/images/GitSSH.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
frontend/assets/images/GithubDarkMode.png
Normal file
BIN
frontend/assets/images/GithubDarkMode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 446 B |
BIN
frontend/assets/images/GithubLightMode.png
Normal file
BIN
frontend/assets/images/GithubLightMode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 675 B |
BIN
frontend/assets/images/Gitlab.png
Normal file
BIN
frontend/assets/images/Gitlab.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
frontend/assets/images/SSHDarkMode.png
Normal file
BIN
frontend/assets/images/SSHDarkMode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 417 B |
BIN
frontend/assets/images/SSHLightMode.png
Normal file
BIN
frontend/assets/images/SSHLightMode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 405 B |
|
|
@ -4,6 +4,8 @@
|
|||
"cancel": "Cancel",
|
||||
"save": "Save",
|
||||
"savechanges": "Save changes",
|
||||
"execute": "Execute",
|
||||
"Build": "Build",
|
||||
"back": "Back",
|
||||
"edit": "Edit",
|
||||
"search": "Search",
|
||||
|
|
|
|||
33221
frontend/package-lock.json
generated
33221
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -187,11 +187,11 @@
|
|||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"jest": "^29.4.2",
|
||||
"node-sass": "^8.0.0",
|
||||
"path": "^0.12.7",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss-loader": "^8.1.0",
|
||||
"prettier": "^2.8.4",
|
||||
"sass": "^1.78.0",
|
||||
"sass-loader": "^13.2.0",
|
||||
"storybook": "^7.2.1",
|
||||
"style-loader": "^3.3.1",
|
||||
|
|
|
|||
|
|
@ -282,9 +282,9 @@ class AppComponent extends React.Component {
|
|||
exact
|
||||
path="/:workspaceId/workflows/*"
|
||||
element={
|
||||
<AdminRoute {...this.props}>
|
||||
<PrivateRoute>
|
||||
<Workflows switchDarkMode={this.switchDarkMode} darkMode={this.darkMode} />
|
||||
</AdminRoute>
|
||||
</PrivateRoute>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -149,10 +149,11 @@ export const Container = React.memo(
|
|||
if (canvasWidth !== undefined) {
|
||||
if (componentType === 'Listview' && listViewMode == 'grid') return canvasWidth / columns - 2;
|
||||
if (id === 'canvas') return canvasWidth;
|
||||
return getSubContainerWidthAfterPadding(canvasWidth, componentType, id);
|
||||
return getSubContainerWidthAfterPadding(canvasWidth, componentType, id, realCanvasRef);
|
||||
}
|
||||
return realCanvasRef?.current?.offsetWidth;
|
||||
}
|
||||
|
||||
const gridWidth = getContainerCanvasWidth() / NO_OF_GRIDS;
|
||||
useEffect(() => {
|
||||
useGridStore.getState().actions.setSubContainerWidths(id, gridWidth);
|
||||
|
|
|
|||
|
|
@ -39,3 +39,5 @@ export const DROPPABLE_PARENTS = new Set([
|
|||
export const TAB_CANVAS_PADDING = 7.5;
|
||||
|
||||
export const MODAL_CANVAS_PADDING = 5;
|
||||
|
||||
export const LISTVIEW_CANVAS_PADDING = 7;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
BOX_PADDING,
|
||||
TAB_CANVAS_PADDING,
|
||||
MODAL_CANVAS_PADDING,
|
||||
LISTVIEW_CANVAS_PADDING,
|
||||
} from './appCanvasConstants';
|
||||
|
||||
export function snapToGrid(canvasWidth, x, y) {
|
||||
|
|
@ -776,7 +777,7 @@ export const getSubContainerIdWithSlots = (parentId) => {
|
|||
return cleanParentId;
|
||||
};
|
||||
|
||||
export const getSubContainerWidthAfterPadding = (canvasWidth, componentType, componentId) => {
|
||||
export const getSubContainerWidthAfterPadding = (canvasWidth, componentType, componentId, realCanvasRef) => {
|
||||
let padding = 2; //Need to update this 2 to correct value for other subcontainers
|
||||
if (componentType === 'Container' || componentType === 'Form') {
|
||||
padding = 2 * CONTAINER_FORM_CANVAS_PADDING + 2 * SUBCONTAINER_CANVAS_BORDER_WIDTH + 2 * BOX_PADDING;
|
||||
|
|
@ -789,11 +790,13 @@ export const getSubContainerWidthAfterPadding = (canvasWidth, componentType, com
|
|||
if (isModalHeader) {
|
||||
const isModalHeaderCloseBtnEnabled = !useStore.getState().getResolvedComponent(componentId)?.properties
|
||||
?.hideCloseButton;
|
||||
console.log('isModalHeaderCloseBtnEnabled', isModalHeaderCloseBtnEnabled);
|
||||
padding = 2 * (MODAL_CANVAS_PADDING + (isModalHeaderCloseBtnEnabled ? 56 : 0));
|
||||
} else {
|
||||
padding = 2 * MODAL_CANVAS_PADDING;
|
||||
}
|
||||
}
|
||||
if (componentType === 'Listview') {
|
||||
padding = 2 * LISTVIEW_CANVAS_PADDING + 5; // 5 is accounting for scrollbar
|
||||
}
|
||||
return canvasWidth - padding;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,11 +16,15 @@ const CreateVersionModal = ({
|
|||
canCommit,
|
||||
orgGit,
|
||||
fetchingOrgGit,
|
||||
handleCommitOnVersionCreation = () => { },
|
||||
handleCommitOnVersionCreation = () => {},
|
||||
}) => {
|
||||
const { moduleId } = useModuleContext();
|
||||
const [isCreatingVersion, setIsCreatingVersion] = useState(false);
|
||||
const [versionName, setVersionName] = useState('');
|
||||
const gitSyncEnabled =
|
||||
orgGit?.org_git?.git_https?.is_enabled ||
|
||||
orgGit?.org_git?.git_ssh?.is_enabled ||
|
||||
orgGit?.org_git?.git_lab?.is_enabled;
|
||||
|
||||
const {
|
||||
createNewVersionAction,
|
||||
|
|
@ -100,8 +104,8 @@ const CreateVersionModal = ({
|
|||
});
|
||||
},
|
||||
(error) => {
|
||||
if (error?.data?.code === "23505") {
|
||||
toast.error("Version name already exists.");
|
||||
if (error?.data?.code === '23505') {
|
||||
toast.error('Version name already exists.');
|
||||
} else {
|
||||
toast.error(error?.error);
|
||||
}
|
||||
|
|
@ -170,7 +174,7 @@ const CreateVersionModal = ({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{orgGit?.org_git?.is_enabled && (
|
||||
{gitSyncEnabled && (
|
||||
<div className="commit-changes" style={{ marginTop: '-1rem', marginBottom: '2rem' }}>
|
||||
<div>
|
||||
<input
|
||||
|
|
|
|||
|
|
@ -6,15 +6,14 @@
|
|||
color: var(--text-default, #1B1F24);
|
||||
overflow-x: auto;
|
||||
min-width: 0;
|
||||
// Hide scrollbar for IE, Edge and Firefox
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
|
||||
// Hide scrollbar for Chrome, Safari and Opera
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Hide scrollbar for IE, Edge and Firefox
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
|
||||
.json-viewer-row-container {
|
||||
min-width: max-content;
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.page-handler.ghost{
|
||||
.page-handler.ghost {
|
||||
background-color: #ECEEF0;
|
||||
&.dark-theme{
|
||||
background-color: var(--slate4);
|
||||
}
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
opacity: 0.8;
|
||||
|
||||
&.dark-theme {
|
||||
background-color: var(--slate4);
|
||||
}
|
||||
}
|
||||
|
||||
.edit-page-overlay-toggle {
|
||||
|
|
@ -40,7 +41,7 @@
|
|||
display: none !important;
|
||||
}
|
||||
|
||||
.page-group-icon-text{
|
||||
.page-group-icon-text {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
|
|
@ -53,16 +54,27 @@
|
|||
}
|
||||
|
||||
.page-handler {
|
||||
.page-menu-item{
|
||||
.page-menu-item {
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0px 8px;
|
||||
margin-top: 2px;
|
||||
justify-content: space-between;
|
||||
|
||||
&.highlight {
|
||||
border: 2px solid #3D63DC;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background-color: #f0f4ff;
|
||||
|
||||
&.dark-theme {
|
||||
background-color: var(--slate5);
|
||||
}
|
||||
}
|
||||
|
||||
.page-group-actions {
|
||||
transition: 0.2s ease;
|
||||
opacity: 0;
|
||||
|
|
@ -70,6 +82,7 @@
|
|||
display: flex;
|
||||
width: unset !important;
|
||||
height: unset !important;
|
||||
|
||||
button {
|
||||
border-radius: var(--2, 4px);
|
||||
border: 1px solid var(--border-default, #CCD1D5);
|
||||
|
|
@ -84,7 +97,8 @@
|
|||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.meta-text{
|
||||
|
||||
.meta-text {
|
||||
color: var(--slate8);
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
|
|
@ -92,33 +106,35 @@
|
|||
line-height: 18px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
button.edit-page-overlay-toggle{
|
||||
|
||||
button.edit-page-overlay-toggle {
|
||||
opacity: 0;
|
||||
}
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background-color: #ECEEF0;
|
||||
button.edit-page-overlay-toggle{
|
||||
|
||||
button.edit-page-overlay-toggle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.page-group-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.dark-theme {
|
||||
&:hover {
|
||||
background-color: var(--slate4);
|
||||
}
|
||||
}
|
||||
}
|
||||
height:32px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0px 8px;
|
||||
margin-top: 2px;
|
||||
justify-content: space-between;
|
||||
.left{
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// margin-left: 15px;
|
||||
.page-name{
|
||||
.page-name {
|
||||
overflow: hidden;
|
||||
color: var(--slate12);
|
||||
font-size: 14px;
|
||||
|
|
@ -127,8 +143,7 @@
|
|||
max-width: 246px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.right {
|
||||
|
|
@ -150,6 +165,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.edit-page-overlay-toggle {
|
||||
opacity: 1;
|
||||
|
|
@ -200,7 +216,7 @@
|
|||
line-height: 18px;
|
||||
}
|
||||
|
||||
.page-menu-action-buttons{
|
||||
.page-menu-action-buttons {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
|
|
@ -211,12 +227,14 @@
|
|||
outline: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
svg path{
|
||||
|
||||
svg path {
|
||||
fill: #6A727C;
|
||||
}
|
||||
&:hover{
|
||||
background: var(--button-outline-hover, rgba(136, 144, 153, 0.12));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--button-outline-hover, rgba(136, 144, 153, 0.12));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -226,7 +244,8 @@
|
|||
border: 1px solid var(--border-weak, #E4E7EB);
|
||||
background: var(--button-secondary, #FFF);
|
||||
box-shadow: 0px 0px 1px 0px var(--dropshadow-100700-layer-1, rgba(48, 50, 51, 0.05)), 0px 1px 1px 0px var(--dropshadow-100400-layer-2, rgba(48, 50, 51, 0.10));
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-default, #CCD1D5);
|
||||
background: linear-gradient(0deg, var(--button-outline-hover, rgba(136, 144, 153, 0.12)) 0%, var(--button-outline-hover, rgba(136, 144, 153, 0.12)) 100%), var(--button-outline, #FFF);
|
||||
|
|
@ -237,18 +256,20 @@
|
|||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 10px;
|
||||
|
||||
&.dark-theme {
|
||||
.popover-body {
|
||||
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.9), 0px 8px 16px 0px #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.popover-body {
|
||||
width: 160px;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
background: var(--background-surface-layer-01);
|
||||
box-shadow: 0px 0px 1px 0px rgba(48, 50, 51, 0.05), 0px 8px 16px 0px rgba(48, 50, 51, 0.1);
|
||||
|
||||
|
||||
.menu-options {
|
||||
.option {
|
||||
display: flex;
|
||||
|
|
@ -260,7 +281,8 @@
|
|||
align-self: stretch;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background: rgba(136, 144, 153, 0.08);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export const ComponentModuleTab = ({ onChangeTab }) => {
|
|||
<button
|
||||
className={`tj-drawer-tabs-btn tj-text-xsm ${activeTab == 1 && 'tj-drawer-tabs-btn-active'}`}
|
||||
onClick={() => handleChangeTab(1)}
|
||||
data-cy="button-invite-with-email"
|
||||
>
|
||||
<span>Components</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import Fuse from 'fuse.js';
|
|||
import { SearchBox } from '@/_components';
|
||||
import { DragLayer } from './DragLayer';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import ComponentModuleTab from './ComponentModuleTab';
|
||||
import { ModuleManager } from '@/modules/Modules/components';
|
||||
import { ComponentModuleTab } from '@/modules/Appbuilder/components';
|
||||
|
||||
// TODO: Hardcode all the component-section mapping in a constant file and just loop over it
|
||||
// TODO: styling
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const listviewConfig = {
|
|||
top: 15,
|
||||
left: 3,
|
||||
height: 100,
|
||||
width: 7,
|
||||
width: 4,
|
||||
},
|
||||
properties: ['source'],
|
||||
accessorKey: 'imageURL',
|
||||
|
|
@ -24,6 +24,7 @@ export const listviewConfig = {
|
|||
top: 50,
|
||||
left: 11,
|
||||
height: 30,
|
||||
width: 4,
|
||||
},
|
||||
properties: ['text'],
|
||||
accessorKey: 'text',
|
||||
|
|
@ -49,12 +50,14 @@ export const listviewConfig = {
|
|||
data: {
|
||||
type: 'code',
|
||||
displayName: 'List data',
|
||||
schema: {
|
||||
type: 'union',
|
||||
schemas: [
|
||||
{ type: 'array', element: { type: 'object' } },
|
||||
{ type: 'array', element: { type: 'string' } },
|
||||
],
|
||||
validation: {
|
||||
schema: {
|
||||
type: 'union',
|
||||
schemas: [
|
||||
{ type: 'array', element: { type: 'object' } },
|
||||
{ type: 'array', element: { type: 'string' } },
|
||||
],
|
||||
},
|
||||
defaultValue: "[{text: 'Sample text 1'}]",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ export const tableConfig = {
|
|||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
|
||||
},
|
||||
defaultSize: {
|
||||
width: 35,
|
||||
width: 25,
|
||||
height: 456,
|
||||
},
|
||||
events: {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ export const Container = ({
|
|||
padding: `${CONTAINER_FORM_CANVAS_PADDING}px ${CONTAINER_FORM_CANVAS_PADDING}px 3px ${CONTAINER_FORM_CANVAS_PADDING}px`,
|
||||
...headerBgColor,
|
||||
};
|
||||
|
||||
const containerContentStyles = {
|
||||
overflow: 'hidden auto',
|
||||
display: 'flex',
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ export const Item = React.memo(
|
|||
>
|
||||
<div className="subcontainer-container" onMouseDown={(e) => e.stopPropagation()}>
|
||||
<SubContainer
|
||||
parentComponent={component}
|
||||
canvasWidth={Number(cardWidth) || 300}
|
||||
canvasHeight={Number(cardHeight) || 100}
|
||||
id={id}
|
||||
|
|
@ -90,11 +89,7 @@ export const Item = React.memo(
|
|||
backgroundColor: 'var(--base)',
|
||||
}}
|
||||
darkMode={darkMode}
|
||||
// parentName={component.name}
|
||||
// customResolvables={{ cardData: cardDataAsObj[value] }}
|
||||
// {...containerProps}
|
||||
// readOnly={isDragActive || !isFirstItem}
|
||||
// parentRef={parentRef}
|
||||
componentType="Kanban"
|
||||
/>
|
||||
</div>
|
||||
<span className="handle-container">
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ export const Listview = function Listview({
|
|||
display: visibility ? 'flex' : 'none',
|
||||
borderRadius: borderRadius ?? 0,
|
||||
boxShadow,
|
||||
padding: '7px 2px 7px 7px',
|
||||
scrollbarGutter: 'stable',
|
||||
};
|
||||
|
||||
const computeCanvasBackgroundColor = useMemo(() => {
|
||||
|
|
@ -235,7 +237,6 @@ export const Listview = function Listview({
|
|||
// Update the customResolvables with the new listItems
|
||||
if (listItems.length > 0) updateCustomResolvables(id, listItems, 'listItem', moduleId);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
data-disabled={disabledState}
|
||||
|
|
@ -243,10 +244,9 @@ export const Listview = function Listview({
|
|||
id={id}
|
||||
ref={parentRef}
|
||||
style={computedStyles}
|
||||
// onClick={() => containerProps.onComponentClick(id, component)}
|
||||
data-cy={dataCy}
|
||||
>
|
||||
<div className={`row w-100 m-0 ${enablePagination && 'pagination-margin-bottom-last-child'}`}>
|
||||
<div className={`w-100 m-0 ${enablePagination && 'pagination-margin-bottom-last-child'}`}>
|
||||
{filteredData.map((listItem, index) => (
|
||||
<div
|
||||
className={`list-item ${mode == 'list' && 'w-100'} ${showBorder && mode == 'list' ? 'border-bottom' : ''}`}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
.tj-datepicker-widget {
|
||||
&.react-datepicker-popper {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--slate5) !important;
|
||||
box-shadow: 0px 32px 64px -12px rgba(16, 24, 40, 0.14);
|
||||
width: 250px;
|
||||
padding: 0px;
|
||||
|
||||
&.react-datepicker-popper {
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.tj-timepicker-widget {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
.tj-datepicker-widget {
|
||||
&.react-datepicker-popper {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--slate5) !important;
|
||||
box-shadow: 0px 32px 64px -12px rgba(16, 24, 40, 0.14);
|
||||
width: 250px;
|
||||
padding: 0px;
|
||||
|
||||
&.react-datepicker-popper {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tj-timepicker-widget {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const listviewConfig = {
|
|||
top: 15,
|
||||
left: 3,
|
||||
height: 100,
|
||||
width: 7,
|
||||
width: 4,
|
||||
},
|
||||
properties: ['source'],
|
||||
accessorKey: 'imageURL',
|
||||
|
|
@ -24,6 +24,7 @@ export const listviewConfig = {
|
|||
top: 50,
|
||||
left: 11,
|
||||
height: 30,
|
||||
width: 4,
|
||||
},
|
||||
properties: ['text'],
|
||||
accessorKey: 'text',
|
||||
|
|
@ -127,7 +128,7 @@ export const listviewConfig = {
|
|||
},
|
||||
styles: {
|
||||
backgroundColor: {
|
||||
type: 'color',
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Background color',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
|
|
@ -135,7 +136,7 @@ export const listviewConfig = {
|
|||
},
|
||||
},
|
||||
borderColor: {
|
||||
type: 'color',
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Border color',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ export const tableConfig = {
|
|||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
|
||||
},
|
||||
defaultSize: {
|
||||
width: 35,
|
||||
width: 25,
|
||||
height: 456,
|
||||
},
|
||||
events: {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class HomePageComponent extends React.Component {
|
|||
id: currentSession?.current_user.id,
|
||||
organization_id: currentSession?.current_organization_id,
|
||||
},
|
||||
shouldRedirect: false,
|
||||
users: null,
|
||||
isLoading: true,
|
||||
creatingApp: false,
|
||||
|
|
@ -131,6 +132,13 @@ class HomePageComponent extends React.Component {
|
|||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.appType === 'workflow') {
|
||||
if (!this.canViewWorkflow()) {
|
||||
toast.error('You do not have permission to view workflows');
|
||||
this.setState({ shouldRedirect: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
fetchAndSetWindowTitle({ page: pageTitles.DASHBOARD });
|
||||
this.fetchApps(1, this.state.currentFolder.id);
|
||||
this.fetchFolders();
|
||||
|
|
@ -148,7 +156,7 @@ class HomePageComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps.appType != this.props.appType) {
|
||||
this.fetchFolders();
|
||||
this.fetchApps(1);
|
||||
|
|
@ -156,6 +164,10 @@ class HomePageComponent extends React.Component {
|
|||
if (Object.keys(this.props.featureAccess).length && !this.state.featureAccess) {
|
||||
this.setState({ featureAccess: this.props.featureAccess, featuresLoaded: this.props.featuresLoaded });
|
||||
}
|
||||
if (this.state.shouldRedirect && !prevState.shouldRedirect) {
|
||||
const workspaceId = getWorkspaceId();
|
||||
this.props.navigate(`/${workspaceId}`);
|
||||
}
|
||||
}
|
||||
|
||||
fetchFeatureAccesss = () => {
|
||||
|
|
@ -450,37 +462,70 @@ class HomePageComponent extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
canViewWorkflow = () => {
|
||||
return this.canUserPerform(this.state.currentUser, 'view');
|
||||
};
|
||||
|
||||
canUserPerform(user, action, app) {
|
||||
if (authenticationService.currentSessionValue?.super_admin) return true;
|
||||
const currentSession = authenticationService.currentSessionValue;
|
||||
const appPermission = currentSession.app_group_permissions;
|
||||
const canUpdateApp =
|
||||
appPermission && (appPermission.is_all_editable || appPermission.editable_apps_id.includes(app?.id));
|
||||
const canReadApp =
|
||||
(appPermission && canUpdateApp) ||
|
||||
appPermission.is_all_viewable ||
|
||||
appPermission.viewable_apps_id.includes(app?.id);
|
||||
let permissionGrant;
|
||||
const { user_permissions, app_group_permissions, workflow_group_permissions, super_admin, admin } = currentSession;
|
||||
|
||||
switch (action) {
|
||||
case 'create':
|
||||
permissionGrant = currentSession.user_permissions.app_create;
|
||||
break;
|
||||
case 'read':
|
||||
permissionGrant = this.isUserOwnerOfApp(user, app) || canReadApp;
|
||||
break;
|
||||
case 'update':
|
||||
permissionGrant = canUpdateApp || this.isUserOwnerOfApp(user, app);
|
||||
break;
|
||||
case 'delete':
|
||||
permissionGrant = currentSession.user_permissions.app_delete || this.isUserOwnerOfApp(user, app);
|
||||
break;
|
||||
default:
|
||||
permissionGrant = false;
|
||||
break;
|
||||
if (super_admin) return true;
|
||||
|
||||
if (this.props.appType === 'workflow') {
|
||||
const canCreateWorkflow = admin || user_permissions?.workflow_create;
|
||||
const canUpdateWorkflow =
|
||||
workflow_group_permissions?.is_all_editable ||
|
||||
workflow_group_permissions?.editable_workflows_id?.includes(app?.id);
|
||||
const canExecuteWorkflow =
|
||||
canUpdateWorkflow ||
|
||||
workflow_group_permissions?.is_all_executable ||
|
||||
workflow_group_permissions?.executable_workflows_id?.includes(app?.id);
|
||||
const canDeleteWorkflow = user_permissions?.workflow_delete || admin;
|
||||
|
||||
switch (action) {
|
||||
case 'create':
|
||||
return canCreateWorkflow;
|
||||
case 'read':
|
||||
return canCreateWorkflow || canUpdateWorkflow || canDeleteWorkflow || canExecuteWorkflow;
|
||||
case 'update':
|
||||
return canUpdateWorkflow;
|
||||
case 'delete':
|
||||
return canDeleteWorkflow;
|
||||
case 'view':
|
||||
return (
|
||||
canCreateWorkflow ||
|
||||
canUpdateWorkflow ||
|
||||
canDeleteWorkflow ||
|
||||
canExecuteWorkflow ||
|
||||
workflow_group_permissions?.editable_workflows_id?.length > 0 ||
|
||||
workflow_group_permissions?.executable_workflows_id?.length > 0
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const canUpdateApp =
|
||||
app_group_permissions &&
|
||||
(app_group_permissions.is_all_editable || app_group_permissions.editable_apps_id.includes(app?.id));
|
||||
const canReadApp =
|
||||
(app_group_permissions && canUpdateApp) ||
|
||||
app_group_permissions.is_all_viewable ||
|
||||
app_group_permissions.viewable_apps_id.includes(app?.id);
|
||||
|
||||
switch (action) {
|
||||
case 'create':
|
||||
return user_permissions.app_create;
|
||||
case 'read':
|
||||
return this.isUserOwnerOfApp(user, app) || canReadApp;
|
||||
case 'update':
|
||||
return canUpdateApp || this.isUserOwnerOfApp(user, app);
|
||||
case 'delete':
|
||||
return user_permissions.app_delete || this.isUserOwnerOfApp(user, app);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return permissionGrant;
|
||||
}
|
||||
|
||||
isUserOwnerOfApp(user, app) {
|
||||
|
|
@ -1518,7 +1563,10 @@ class HomePageComponent extends React.Component {
|
|||
) : (
|
||||
<p className="empty-title mt-3">
|
||||
You have not created any modules.
|
||||
<a onClick={this.openCreateAppModal} className="text-bold">
|
||||
<a
|
||||
onClick={this.openCreateAppModal}
|
||||
className={`text-bold ${this.props.appType === 'module' && invalidLicense ? 'disabled' : ''}`}
|
||||
>
|
||||
Create a module
|
||||
</a>
|
||||
to start using it within your apps.
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
// margin-right: 6px !important;
|
||||
// }
|
||||
|
||||
.tj-database-column-header:hover{
|
||||
.primaryKeyTooltip{
|
||||
.tj-database-column-header:hover {
|
||||
.primaryKeyTooltip {
|
||||
width: 88% !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,9 @@
|
|||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 0px;
|
||||
border-color: inherit;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
|
@ -68,8 +71,6 @@
|
|||
background-color: rgba(101, 109, 119, 0.16) !important;
|
||||
}
|
||||
|
||||
border-right: 0px;
|
||||
border-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +113,10 @@
|
|||
}
|
||||
|
||||
&:last-child {
|
||||
|
||||
border-right: 0px;
|
||||
border-color: inherit;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
|
@ -122,9 +127,6 @@
|
|||
height: 100%;
|
||||
background-color: rgba(101, 109, 119, 0.16) !important;
|
||||
}
|
||||
|
||||
border-right: 0px;
|
||||
border-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -168,18 +170,20 @@
|
|||
width: 80%;
|
||||
}
|
||||
}
|
||||
&:has(.tjdb-dashboard-codehinter-wrapper-cell){
|
||||
|
||||
&:has(.tjdb-dashboard-codehinter-wrapper-cell) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.tjdb-dashboard-codehinter-wrapper-cell{
|
||||
.cm-editor{
|
||||
border: 0 !important;
|
||||
}
|
||||
.codehinter-input.focused .cm-editor{
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.tjdb-dashboard-codehinter-wrapper-cell {
|
||||
.cm-editor {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.codehinter-input.focused .cm-editor {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,10 +195,10 @@
|
|||
padding-bottom: 0px !important;
|
||||
|
||||
.cell-menu-text {
|
||||
white-space: nowrap ;
|
||||
overflow: hidden ;
|
||||
text-overflow: ellipsis ;
|
||||
width: 85% ;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 85%;
|
||||
color: var(--slate11) !important;
|
||||
cursor: default;
|
||||
border-radius: 6px;
|
||||
|
|
@ -209,6 +213,7 @@
|
|||
.tjdb-td-wrapper {
|
||||
div {
|
||||
padding-bottom: 0px !important;
|
||||
|
||||
.cell-menu-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
|
@ -431,13 +436,15 @@
|
|||
background: #1C252F !important;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar{
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
&::-webkit-scrollbar-track{
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
&:hover::-webkit-scrollbar{
|
||||
|
||||
&:hover::-webkit-scrollbar {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@
|
|||
}
|
||||
|
||||
.toggle-switch-container {
|
||||
margin-top: 30px;
|
||||
margin-top: 0px;
|
||||
display: flex;
|
||||
margin-bottom: 16px !important;
|
||||
|
||||
.switch {
|
||||
margin-top: 3px;
|
||||
|
|
@ -63,4 +64,14 @@
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-id-group-sync-container {
|
||||
border-top:1px solid var(--slate5) !important;
|
||||
margin-top: 12px !important;
|
||||
.custom-card-body {
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 0px !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const currentSessionSubject = new BehaviorSubject({
|
|||
group_permissions: null,
|
||||
app_group_permissions: null,
|
||||
data_source_group_permissions: null,
|
||||
workflow_group_permissions: null,
|
||||
role: null,
|
||||
organizations: [],
|
||||
isUserLoggingIn: false,
|
||||
|
|
|
|||
|
|
@ -16,12 +16,16 @@ export const gitSyncService = {
|
|||
confirmPullChanges,
|
||||
updateStatus,
|
||||
getGitStatus,
|
||||
saveProviderConfigs,
|
||||
updateAppEditState,
|
||||
getAppGitConfigs,
|
||||
};
|
||||
|
||||
function create(organizationId, gitUrl) {
|
||||
function create(organizationId, gitUrl, gitType) {
|
||||
const body = {
|
||||
organizationId,
|
||||
gitUrl,
|
||||
gitType,
|
||||
};
|
||||
|
||||
const requestOptions = {
|
||||
|
|
@ -33,7 +37,7 @@ function create(organizationId, gitUrl) {
|
|||
return fetch(`${config.apiUrl}/git-sync`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function updateConfig(organizationGitId, updateParam) {
|
||||
function updateConfig(organizationGitId, updateParam, gitType = '') {
|
||||
const { gitUrl, autoCommit, keyType } = updateParam;
|
||||
const body = {
|
||||
...(gitUrl && { gitUrl }),
|
||||
|
|
@ -46,12 +50,15 @@ function updateConfig(organizationGitId, updateParam) {
|
|||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/${organizationGitId}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/git-sync/${organizationGitId}?gitType=${gitType}`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
}
|
||||
|
||||
function updateStatus(organizationGitId, isEnabled) {
|
||||
function updateStatus(organizationGitId, isEnabled, gitType) {
|
||||
const body = {
|
||||
isEnabled,
|
||||
gitType,
|
||||
};
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
|
|
@ -62,31 +69,13 @@ function updateStatus(organizationGitId, isEnabled) {
|
|||
return fetch(`${config.apiUrl}/git-sync/status/${organizationGitId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function setFinalizeConfig(organizationGitId) {
|
||||
const controller = new AbortController();
|
||||
const timeOut = 2500;
|
||||
const id = setTimeout(() => controller.abort(), timeOut);
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
const response = fetch(`${config.apiUrl}/git-sync/finalize/${organizationGitId}`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
clearTimeout(id);
|
||||
return response;
|
||||
}
|
||||
|
||||
function getGitConfig(workspaceId) {
|
||||
function getGitConfig(workspaceId, gitType = '') {
|
||||
const requestOptions = {
|
||||
method: 'GET',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/${workspaceId}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/git-sync/${workspaceId}?gitType=${gitType}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function getGitStatus(workspaceId) {
|
||||
|
|
@ -107,13 +96,15 @@ function syncAppVersion(appGitId, versionId) {
|
|||
return fetch(`${config.apiUrl}/app-git/${appGitId}/sync/${versionId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function deleteConfig(organizationGitId) {
|
||||
function deleteConfig(organizationGitId, gitType) {
|
||||
const requestOptions = {
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/${organizationGitId}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/git-sync/${organizationGitId}?gitType=${gitType}`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
}
|
||||
|
||||
function gitPush(body, appGitId, versionId) {
|
||||
|
|
@ -123,7 +114,7 @@ function gitPush(body, appGitId, versionId) {
|
|||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/gitpush/${appGitId}/${versionId}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/app-git/gitpush/${appGitId}/${versionId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function getAppConfig(organizationId, versionId) {
|
||||
|
|
@ -136,7 +127,7 @@ function getAppConfig(organizationId, versionId) {
|
|||
credentials: 'include',
|
||||
signal: controller.signal,
|
||||
};
|
||||
const response = fetch(`${config.apiUrl}/git-sync/${organizationId}/app/${versionId}`, requestOptions).then(
|
||||
const response = fetch(`${config.apiUrl}/app-git/${organizationId}/app/${versionId}`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
clearTimeout(id);
|
||||
|
|
@ -149,7 +140,7 @@ function checkForUpdates(appId) {
|
|||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/gitpull/app/${appId}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/app-git/gitpull/app/${appId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function gitPull() {
|
||||
|
|
@ -158,7 +149,7 @@ function gitPull() {
|
|||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/gitpull`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/app-git/gitpull`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function confirmPullChanges(body, appId) {
|
||||
|
|
@ -168,7 +159,7 @@ function confirmPullChanges(body, appId) {
|
|||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/gitpull/app/${appId}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/app-git/gitpull/app/${appId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function importGitApp(body) {
|
||||
|
|
@ -178,5 +169,58 @@ function importGitApp(body) {
|
|||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/gitpull/app`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/app-git/gitpull/app`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function setFinalizeConfig(organizationGitId, body) {
|
||||
const controller = new AbortController();
|
||||
const timeOut = 2500;
|
||||
const id = setTimeout(() => controller.abort(), timeOut);
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
const response = fetch(`${config.apiUrl}/git-sync/finalize/${organizationGitId}`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
clearTimeout(id);
|
||||
return response;
|
||||
}
|
||||
function saveProviderConfigs(body) {
|
||||
// TO DO Later : Review if we need to use abort controller for this api request
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/configs`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function updateAppEditState(appId, allowEditing) {
|
||||
const body = {
|
||||
allowEditing,
|
||||
};
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/git-sync/appGit/${appId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
function getAppGitConfigs(workspaceId, versionId) {
|
||||
const requestOptions = {
|
||||
method: 'GET',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
};
|
||||
|
||||
return fetch(`${config.apiUrl}/git-sync/${workspaceId}/app/${versionId}/configs`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
}
|
||||
// Remove all app-git api's to separate service from here.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ $btn-dark-color: #FFFFFF;
|
|||
transition: all 0.3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
@if $bg !=none {
|
||||
@if $bg !=none {
|
||||
background-color: darken($bg, 10%);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,13 +26,17 @@ $btn-dark-color: #FFFFFF;
|
|||
|
||||
.base-button {
|
||||
@include button($btn-bg, $btn-color);
|
||||
border-radius: $base-border-radius;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4px 16px;
|
||||
border: 1px solid #D7DBDF;
|
||||
|
||||
& {
|
||||
border-radius: $base-border-radius;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4px 16px;
|
||||
border: 1px solid #D7DBDF;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.base-button.dark {
|
||||
|
|
@ -52,11 +56,14 @@ $btn-dark-color: #FFFFFF;
|
|||
|
||||
.unstyled-button {
|
||||
@include button(none, inherit);
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
|
||||
& {
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.unstyled-button.dark {
|
||||
|
|
@ -278,74 +285,86 @@ $btn-dark-color: #FFFFFF;
|
|||
}
|
||||
}
|
||||
|
||||
.delete-folder-modal{
|
||||
&.dark-theme{
|
||||
.delete-all-button{
|
||||
&:hover{
|
||||
.delete-folder-modal {
|
||||
&.dark-theme {
|
||||
.delete-all-button {
|
||||
&:hover {
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
border: 1px solid rgb(77, 114, 250);
|
||||
}
|
||||
}
|
||||
}
|
||||
.delete-all-button{
|
||||
|
||||
.delete-all-button {
|
||||
margin: 0;
|
||||
border-radius: 6px;
|
||||
border: 1px solid#CCD1D5;
|
||||
background:#FFF;
|
||||
background: #FFF;
|
||||
box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.10);
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background: rgba(172, 178, 185, 0.30);
|
||||
box-shadow: none;
|
||||
|
||||
}
|
||||
span{
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 150% */
|
||||
line-height: 18px;
|
||||
/* 150% */
|
||||
}
|
||||
&.danger{
|
||||
|
||||
&.danger {
|
||||
border-radius: 6px;
|
||||
background: #D72D39;
|
||||
border:none;
|
||||
border: none;
|
||||
|
||||
box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.10);
|
||||
span{
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
background: #B5121D;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.modal-title{
|
||||
|
||||
.modal-title {
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
.modal-dialog{
|
||||
|
||||
.modal-dialog {
|
||||
width: 408px;
|
||||
}
|
||||
.modal-body{
|
||||
|
||||
.modal-body {
|
||||
padding: 16px 0;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
.modal-content{
|
||||
|
||||
.modal-content {
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
|
||||
}
|
||||
.modal-header{
|
||||
|
||||
.modal-header {
|
||||
padding: 0;
|
||||
min-height: unset;
|
||||
border: none;
|
||||
|
|
@ -354,7 +373,8 @@ $btn-dark-color: #FFFFFF;
|
|||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
.modal-footer{
|
||||
|
||||
.modal-footer {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
justify-content: unset;
|
||||
|
|
@ -403,7 +423,7 @@ $btn-dark-color: #FFFFFF;
|
|||
}
|
||||
|
||||
.page-handler-input {
|
||||
border-radius: $base-border-radius !important;
|
||||
border-radius: $base-border-radius !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,12 +500,12 @@ $btn-dark-color: #FFFFFF;
|
|||
|
||||
#page-handler-menu.global-settings {
|
||||
background-color: var(--base);
|
||||
border: none;
|
||||
|
||||
.popover-body {
|
||||
color: var(--slate12);
|
||||
}
|
||||
|
||||
border: none;
|
||||
}
|
||||
|
||||
#inspector-tabpane-properties .code-hinter textarea {
|
||||
|
|
@ -495,14 +515,17 @@ $btn-dark-color: #FFFFFF;
|
|||
}
|
||||
|
||||
// Style for Chart component
|
||||
.widget-chart{
|
||||
.js-plotly-plot{
|
||||
.widget-chart {
|
||||
.js-plotly-plot {
|
||||
border-radius: inherit;
|
||||
.plot-container{
|
||||
|
||||
.plot-container {
|
||||
border-radius: inherit;
|
||||
.svg-container{
|
||||
|
||||
.svg-container {
|
||||
border-radius: inherit;
|
||||
.main-svg{
|
||||
|
||||
.main-svg {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
|
|
@ -514,6 +537,7 @@ $btn-dark-color: #FFFFFF;
|
|||
.widget-type-container {
|
||||
overflow: hidden auto;
|
||||
scrollbar-width: none;
|
||||
|
||||
&:hover {
|
||||
scrollbar-width: auto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@
|
|||
}
|
||||
|
||||
.apps-modules-tabs.dark-mode {
|
||||
border-bottom-color: #2B394A;
|
||||
|
||||
.nav-link {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
border-bottom-color: #2B394A;
|
||||
}
|
||||
|
||||
#homePage-tab-front-end,
|
||||
|
|
@ -44,6 +44,9 @@
|
|||
|
||||
.module-container-canvas {
|
||||
>div:first-child {
|
||||
/* Firefox scrollbar support */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: transparent transparent;
|
||||
height: 100%;
|
||||
overflow: hidden auto;
|
||||
|
||||
|
|
@ -65,10 +68,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Firefox scrollbar support */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: transparent transparent;
|
||||
|
||||
&:hover {
|
||||
scrollbar-color: #6a727c4d transparent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -500,7 +500,7 @@
|
|||
line-break: anywhere;
|
||||
text-align: center;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
.signup-password-wrap,
|
||||
|
|
@ -1603,13 +1603,14 @@
|
|||
gap: 20px;
|
||||
|
||||
.body-row {
|
||||
.feature-title {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 0.2fr 0.1fr;
|
||||
|
||||
.feature-title {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
$border-radius: 4px;
|
||||
|
||||
.query-manager {
|
||||
user-select: none;
|
||||
|
||||
.react-select__value-container {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
user-select: none;
|
||||
|
||||
.btn {
|
||||
height: 31px;
|
||||
}
|
||||
|
|
@ -89,6 +89,20 @@ $border-radius: 4px;
|
|||
}
|
||||
|
||||
.query-pane {
|
||||
|
||||
height: 400px;
|
||||
position: fixed;
|
||||
left: 48px;
|
||||
right: 300px;
|
||||
bottom: 0;
|
||||
overflow-x: hidden;
|
||||
flex: 1 1 auto;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
border-top: 1px solid var(--slate5);
|
||||
border-width: 1px 0px 0px 0px;
|
||||
background-color: var(--base);
|
||||
|
||||
&.expanded {
|
||||
z-index: 12 !important;
|
||||
}
|
||||
|
|
@ -107,19 +121,6 @@ $border-radius: 4px;
|
|||
}
|
||||
}
|
||||
|
||||
height: 400px;
|
||||
position: fixed;
|
||||
left: 48px;
|
||||
right: 300px;
|
||||
bottom: 0;
|
||||
overflow-x: hidden;
|
||||
flex: 1 1 auto;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
border-top: 1px solid var(--slate5);
|
||||
border-width: 1px 0px 0px 0px;
|
||||
background-color: var(--base);
|
||||
|
||||
&#query-manager {
|
||||
border-width: 3px 0px 0px 0px;
|
||||
}
|
||||
|
|
@ -1129,6 +1130,8 @@ $border-radius: 4px;
|
|||
justify-content: space-between;
|
||||
|
||||
.left {
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
color: var(--slate12);
|
||||
font-family: 'source-code-pro', Menlo, 'Courier New', Consolas, monospace;
|
||||
|
|
@ -1143,7 +1146,6 @@ $border-radius: 4px;
|
|||
fill: var(--slate12) !important;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -222,6 +222,8 @@
|
|||
|
||||
.td {
|
||||
|
||||
display: flex !important;
|
||||
|
||||
// padding: 6px 12px;
|
||||
.text-container:focus-visible,
|
||||
.text-container:focus,
|
||||
|
|
@ -237,8 +239,6 @@
|
|||
}
|
||||
|
||||
|
||||
display: flex !important;
|
||||
|
||||
.td-container {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
|
|
@ -317,6 +317,7 @@
|
|||
}
|
||||
|
||||
.jet-data-table {
|
||||
overflow: hidden;
|
||||
|
||||
.th:first-child,
|
||||
td:first-child {
|
||||
|
|
@ -328,8 +329,6 @@
|
|||
border-right: none;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
.form-check {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
@ -719,6 +718,7 @@
|
|||
width: 100%;
|
||||
color: var(--slate12) !important;
|
||||
height: 100% !important;
|
||||
overflow-wrap: wrap;
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
|
|
@ -734,7 +734,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
overflow-wrap: wrap;
|
||||
|
||||
.dropdown-item {
|
||||
&:hover {
|
||||
|
|
@ -1148,9 +1147,12 @@
|
|||
}
|
||||
|
||||
.table-select-custom-menu-list {
|
||||
box-shadow: var(--elevation-400-box-shadow) !important;
|
||||
|
||||
.react-select__menu-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--surfaces-surface-01) !important;
|
||||
|
||||
.option-wrapper {
|
||||
align-items: center;
|
||||
|
|
@ -1185,10 +1187,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
background-color: var(--surfaces-surface-01) !important;
|
||||
}
|
||||
|
||||
box-shadow: var(--elevation-400-box-shadow) !important;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4147,12 +4147,12 @@ input[type="text"] {
|
|||
}
|
||||
|
||||
.rbc-event {
|
||||
background-color: var(--primary-brand) !important;
|
||||
border: transparent;
|
||||
|
||||
.rbc-event-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
background-color: var(--primary-brand) !important;
|
||||
border: transparent
|
||||
}
|
||||
|
||||
.rbc-off-range-bg {
|
||||
|
|
@ -4274,7 +4274,6 @@ input[type="text"] {
|
|||
|
||||
.jet-listview::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
|
||||
}
|
||||
|
||||
.jet-listview::-webkit-scrollbar-thumb {
|
||||
|
|
@ -4358,6 +4357,11 @@ input[type="text"] {
|
|||
left: 50%;
|
||||
top: 5%;
|
||||
z-index: 1400;
|
||||
transform: translate(-60%, 0%);
|
||||
height: 350px;
|
||||
width: auto;
|
||||
max-height: 500px;
|
||||
padding: 0px;
|
||||
|
||||
.modal-body {
|
||||
width: 500px !important;
|
||||
|
|
@ -4365,11 +4369,6 @@ input[type="text"] {
|
|||
padding: 0px !important;
|
||||
}
|
||||
|
||||
transform: translate(-60%, 0%);
|
||||
height: 350px;
|
||||
width: auto;
|
||||
max-height: 500px;
|
||||
padding: 0px;
|
||||
|
||||
.modal-content {
|
||||
border-radius: 5px !important;
|
||||
|
|
@ -5144,8 +5143,12 @@ input[type="text"] {
|
|||
|
||||
.button-family-secondary {
|
||||
@include button-outline($light-theme: true);
|
||||
height: 32px;
|
||||
width: 112px;
|
||||
|
||||
& {
|
||||
|
||||
height: 32px;
|
||||
width: 112px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-family-secondary.dark {
|
||||
|
|
@ -5662,7 +5665,7 @@ div#driver-page-overlay {
|
|||
}
|
||||
|
||||
.open-id-sso-card {
|
||||
border-bottom: 1px solid var(--slate5) !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.sso-card-footer {
|
||||
|
|
@ -9208,12 +9211,13 @@ tbody {
|
|||
}
|
||||
|
||||
.git-sync-btn.disabled-action-tooltip {
|
||||
opacity: 1;
|
||||
background: var(--slate3);
|
||||
|
||||
.license-tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
opacity: 1;
|
||||
background: var(--slate3);
|
||||
|
||||
rect {
|
||||
fill: var(--slate3);
|
||||
|
|
@ -10656,7 +10660,6 @@ tbody {
|
|||
.manage-groups-body {
|
||||
padding: 12px 12px 10px 12px;
|
||||
font-size: 12px;
|
||||
overflow-y: auto;
|
||||
height: calc(100vh - 300px);
|
||||
|
||||
.group-users-list-container {
|
||||
|
|
@ -10877,6 +10880,7 @@ tbody {
|
|||
}
|
||||
|
||||
.permission-body {
|
||||
|
||||
.form-check {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
|
@ -13992,16 +13996,17 @@ tbody {
|
|||
|
||||
.tj-foreignKey {
|
||||
.tj-secondary-btn {
|
||||
font-size: 12px;
|
||||
background: transparent !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: #3e63dd !important
|
||||
}
|
||||
}
|
||||
|
||||
font-size: 12px;
|
||||
background: transparent !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14023,16 +14028,17 @@ tbody {
|
|||
|
||||
.tj-foreignKey {
|
||||
.tj-secondary-btn {
|
||||
font-size: 12px;
|
||||
background: transparent !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: #3e63dd !important
|
||||
}
|
||||
}
|
||||
|
||||
font-size: 12px;
|
||||
background: transparent !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14049,16 +14055,17 @@ tbody {
|
|||
|
||||
.tj-foreignKey {
|
||||
.tj-secondary-btn {
|
||||
font-size: 12px;
|
||||
background: transparent !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: #3e63dd !important
|
||||
}
|
||||
}
|
||||
|
||||
font-size: 12px;
|
||||
background: transparent !important;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16040,9 +16047,7 @@ textarea.tj-text-input-widget {
|
|||
}
|
||||
|
||||
.jet-listview {
|
||||
&:hover {
|
||||
scrollbar-color: #6a727c4d;
|
||||
|
||||
&:hover {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #6a727c4d !important;
|
||||
}
|
||||
|
|
@ -16062,7 +16067,6 @@ textarea.tj-text-input-widget {
|
|||
|
||||
.jet-listview {
|
||||
&:hover {
|
||||
scrollbar-color: #6a727c4d;
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #6a727c4d !important;
|
||||
|
|
@ -16328,6 +16332,7 @@ fieldset:disabled {
|
|||
|
||||
|
||||
.date-validation-wrapper {
|
||||
margin-bottom: 3px;
|
||||
|
||||
.field {
|
||||
height: 24px;
|
||||
|
|
@ -16337,7 +16342,6 @@ fieldset:disabled {
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.tj-inspector-datepicker {
|
||||
|
|
@ -16744,6 +16748,8 @@ fieldset:disabled {
|
|||
background-color: transparent !important;
|
||||
border: none;
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #f1f1f1 !important;
|
||||
|
|
@ -16755,8 +16761,6 @@ fieldset:disabled {
|
|||
// box-shadow: 0px 0px 0px 2px var(--Interactive-focusActive, #4368E3);
|
||||
// }
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button.submit {
|
||||
|
|
@ -16910,6 +16914,8 @@ section.ai-message-prompt-input-wrapper {
|
|||
background-color: transparent !important;
|
||||
border: none;
|
||||
outline: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #f1f1f1 !important;
|
||||
|
|
@ -16921,8 +16927,6 @@ section.ai-message-prompt-input-wrapper {
|
|||
// box-shadow: 0px 0px 0px 2px var(--Interactive-focusActive, #4368E3);
|
||||
// }
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button.submit {
|
||||
|
|
@ -17445,6 +17449,15 @@ section.ai-message-prompt-input-wrapper {
|
|||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
transition: right 0.3s ease;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
|
||||
border-left: 1px solid var(--border-weak, #E4E7EB);
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 0px 1px 0px var(--dropshadow-100700-layer-1, rgba(48, 50, 51, 0.05)),
|
||||
0px 8px 16px 0px var(--dropshadow-100400-layer-2, rgba(48, 50, 51, 0.10));
|
||||
|
||||
.input-box-gradient {
|
||||
position: absolute;
|
||||
|
|
@ -17471,15 +17484,6 @@ section.ai-message-prompt-input-wrapper {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
height: 100%;
|
||||
transition: right 0.3s ease;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
|
||||
border-left: 1px solid var(--border-weak, #E4E7EB);
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 0px 1px 0px var(--dropshadow-100700-layer-1, rgba(48, 50, 51, 0.05)),
|
||||
0px 8px 16px 0px var(--dropshadow-100400-layer-2, rgba(48, 50, 51, 0.10));
|
||||
|
||||
&.dark-theme {
|
||||
background-color: #1f2936;
|
||||
|
|
@ -17666,6 +17670,12 @@ section.ai-message-prompt-input-wrapper {
|
|||
}
|
||||
|
||||
.suggestion {
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 6px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--text-default, #1B1F24);
|
||||
|
|
@ -17674,11 +17684,6 @@ section.ai-message-prompt-input-wrapper {
|
|||
font-weight: 400;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
padding: 4px 8px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 6px;
|
||||
|
||||
// &:hover {
|
||||
// background: var(--interactive-hover, rgba(136, 144, 153, 0.12));
|
||||
|
|
@ -18181,11 +18186,6 @@ section.ai-message-prompt-input-wrapper {
|
|||
display: inline-block;
|
||||
border-radius: 8px;
|
||||
background: #F5F6F7;
|
||||
|
||||
&.dark-theme {
|
||||
background-color: #1B1F24;
|
||||
}
|
||||
|
||||
padding: 8px 16px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
|
@ -18198,6 +18198,11 @@ section.ai-message-prompt-input-wrapper {
|
|||
line-height: 18px;
|
||||
margin-left: auto;
|
||||
|
||||
&.dark-theme {
|
||||
background-color: #1B1F24;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18206,9 +18211,6 @@ section.ai-message-prompt-input-wrapper {
|
|||
}
|
||||
|
||||
section.template-cards {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
overflow-x: auto;
|
||||
margin-top: 6px;
|
||||
|
|
@ -18217,6 +18219,10 @@ section.ai-message-prompt-input-wrapper {
|
|||
gap: 16px;
|
||||
display: flex;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
flex: 0 0 auto;
|
||||
// display: flex;
|
||||
|
|
@ -19005,4 +19011,4 @@ section.ai-message-prompt-input-wrapper {
|
|||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
@import "../colors.scss";
|
||||
|
||||
.handle {
|
||||
display: flex;
|
||||
width: 12px;
|
||||
|
|
@ -54,6 +55,7 @@ $focused-outline-color: #4c9ffe;
|
|||
transform: scale(1);
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(var(--scale));
|
||||
box-shadow: var(--box-shadow-picked-up);
|
||||
|
|
@ -64,6 +66,7 @@ $focused-outline-color: #4c9ffe;
|
|||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
@ -72,8 +75,7 @@ $focused-outline-color: #4c9ffe;
|
|||
.kanban-item {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
transform: translate3d(var(--translate-x, 0), var(--translate-y, 0), 0)
|
||||
scaleX(var(--scale-x, 1)) scaleY(var(--scale-y, 1));
|
||||
transform: translate3d(var(--translate-x, 0), var(--translate-y, 0), 0) scaleX(var(--scale-x, 1)) scaleY(var(--scale-y, 1));
|
||||
transform-origin: 0 0;
|
||||
touch-action: manipulation;
|
||||
min-width: 100px;
|
||||
|
|
@ -115,7 +117,7 @@ $focused-outline-color: #4c9ffe;
|
|||
transform: scale(1);
|
||||
transition: box-shadow 200ms cubic-bezier(0.18, 0.67, 0.6, 1.22);
|
||||
|
||||
&.dark-light{
|
||||
&.dark-light {
|
||||
border: 1px solid rgba(229, 229, 229, 0.08);
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +131,7 @@ $focused-outline-color: #4c9ffe;
|
|||
}
|
||||
|
||||
&.dragging:not(.dragOverlay) {
|
||||
opacity: 0.5;
|
||||
opacity: 0.5;
|
||||
z-index: 0;
|
||||
|
||||
&:focus {
|
||||
|
|
@ -138,12 +140,13 @@ $focused-outline-color: #4c9ffe;
|
|||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: #999;
|
||||
background-color: #f1f1f1;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0px 4px 1px rgba(0, 0, 0, 0.1), $box-shadow;
|
||||
}
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.dragOverlay {
|
||||
|
|
@ -154,7 +157,8 @@ $focused-outline-color: #4c9ffe;
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.subcontainer-container{
|
||||
|
||||
.subcontainer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
|
@ -165,6 +169,7 @@ $focused-outline-color: #4c9ffe;
|
|||
top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.kanban-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -182,7 +187,7 @@ $focused-outline-color: #4c9ffe;
|
|||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
font-size: 1em;
|
||||
|
||||
.container-name{
|
||||
.container-name {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
@ -213,6 +218,7 @@ $focused-outline-color: #4c9ffe;
|
|||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.hover {
|
||||
background-color: rgba(235, 235, 235, 1);
|
||||
}
|
||||
|
|
@ -234,6 +240,7 @@ $focused-outline-color: #4c9ffe;
|
|||
&.shadow {
|
||||
box-shadow: 0 1px 10px 0 rgba(34, 33, 81, 0.1);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
padding: 10px 20px;
|
||||
|
|
@ -244,18 +251,20 @@ $focused-outline-color: #4c9ffe;
|
|||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
&.dark{
|
||||
|
||||
&.dark {
|
||||
border-bottom: 1px solid rgba(229, 229, 229, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
|
||||
.add-card-btn{
|
||||
.add-card-btn {
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +276,8 @@ $focused-outline-color: #4c9ffe;
|
|||
right: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
.kanban-add-card-button{
|
||||
|
||||
.kanban-add-card-button {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
|
|
@ -275,9 +285,10 @@ $focused-outline-color: #4c9ffe;
|
|||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.dark-light{
|
||||
background-color: $bg-dark-light !important;
|
||||
}
|
||||
.dark{
|
||||
background-color: $bg-dark !important;
|
||||
.dark-light {
|
||||
background-color: $bg-dark-light !important;
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: $bg-dark !important;
|
||||
}
|
||||
|
|
@ -6,10 +6,46 @@ import Select, { components } from 'react-select';
|
|||
import { FilterPreview } from '@/_components';
|
||||
import './appSelect.theme.scss';
|
||||
|
||||
export const RESOURCE_TYPE = {
|
||||
APPS: 'app',
|
||||
DATA_SOURCES: 'data_source',
|
||||
WORKFLOWS: 'workflow',
|
||||
};
|
||||
|
||||
export const getResourceTypeConfig = (resourceType) => {
|
||||
switch (resourceType) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return {
|
||||
placeholder: 'Select apps..',
|
||||
noOptionsMessage: 'No apps found',
|
||||
icon: 'apps',
|
||||
};
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
return {
|
||||
placeholder: 'Select data sources..',
|
||||
noOptionsMessage: 'No data sources found',
|
||||
icon: 'datasource',
|
||||
};
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return {
|
||||
placeholder: 'Select workflows..',
|
||||
noOptionsMessage: 'No workflows found',
|
||||
icon: 'workflows',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
placeholder: 'Select resources..',
|
||||
noOptionsMessage: 'No resources found',
|
||||
icon: 'apps',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export function AppsSelect(props) {
|
||||
const navigate = useNavigate();
|
||||
const workspaceId = getWorkspaceId();
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const resourceConfig = getResourceTypeConfig(props.resourceType);
|
||||
|
||||
//Will be used when workspace routing settings have been merged
|
||||
const Menu = (props) => {
|
||||
|
|
@ -188,8 +224,8 @@ export function AppsSelect(props) {
|
|||
}}
|
||||
options={[props.allowSelectAll ? props.allOption : null, ...props.options]}
|
||||
styles={selectStyles}
|
||||
placeholder={props.resourceType === 'Apps' ? 'Select apps..' : 'Select data sources..'}
|
||||
noOptionsMessage={() => 'No apps found'}
|
||||
placeholder={resourceConfig.placeholder}
|
||||
noOptionsMessage={() => resourceConfig.noOptionsMessage}
|
||||
/>
|
||||
// </div>
|
||||
);
|
||||
|
|
@ -201,4 +237,5 @@ AppsSelect.defaultProps = {
|
|||
value: '*',
|
||||
isAllField: true,
|
||||
},
|
||||
resourceType: RESOURCE_TYPE.APPS,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
import { withEditionSpecificComponent } from '@/modules/common/helpers';
|
||||
import BaseComponentModuleTab from '@/modules/common/components/BaseComponentModuleTab';
|
||||
|
||||
const ComponentModuleTab = (props) => {
|
||||
return <BaseComponentModuleTab {...props} />;
|
||||
};
|
||||
|
||||
export default withEditionSpecificComponent(ComponentModuleTab, 'Appbuilder');
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './ComponentModuleTab';
|
||||
|
|
@ -5,6 +5,7 @@ import AppEnvironments from './AppEnvironments';
|
|||
import ThemeSelect from './ThemeSelect';
|
||||
import ColorSwatches from './ColorSwatches';
|
||||
import AppPermissionsModal from './AppPermissionsModal';
|
||||
import ComponentModuleTab from './ComponentModuleTab';
|
||||
|
||||
export {
|
||||
CreateVersionModal,
|
||||
|
|
@ -14,4 +15,5 @@ export {
|
|||
ThemeSelect,
|
||||
ColorSwatches,
|
||||
AppPermissionsModal,
|
||||
ComponentModuleTab,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ import AddResourcePermissionsMenu from './components/AddResourcePermissionsMenu'
|
|||
import { ConfirmDialog } from '@/_components';
|
||||
import AddEditResourcePermissionsModal from './components/AddEditResourceModal/AddEditResourcePermissionsModal';
|
||||
import DataSourceResourcePermissions from './components/DataSourceResourcePermission';
|
||||
import WorkflowResourcePermissions from './components/WorkflowResourcePermission';
|
||||
import Spinner from 'react-bootstrap/Spinner';
|
||||
import { RESOURCE_TYPE, APP_TYPES, RESOURCE_NAME_MAPPING } from '../..';
|
||||
|
||||
class BaseManageGranularAccess extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -24,7 +26,7 @@ class BaseManageGranularAccess extends React.Component {
|
|||
errors: {},
|
||||
values: {},
|
||||
customSelected: true,
|
||||
selectedApps: [],
|
||||
selectedResources: [],
|
||||
type: null,
|
||||
newPermissionName: null,
|
||||
initialPermissionState: {
|
||||
|
|
@ -47,13 +49,11 @@ class BaseManageGranularAccess extends React.Component {
|
|||
updateType: '',
|
||||
deleteConfirmationModal: false,
|
||||
deletingPermissions: false,
|
||||
|
||||
initialPermissionStateDs: {
|
||||
canUse: false,
|
||||
canView: false,
|
||||
},
|
||||
selectedDs: [],
|
||||
resourceType: '',
|
||||
resourceType: null,
|
||||
hasChanges: false,
|
||||
initialState: {
|
||||
type: 'app',
|
||||
|
|
@ -66,8 +66,7 @@ class BaseManageGranularAccess extends React.Component {
|
|||
canUse: false,
|
||||
canConfigure: false,
|
||||
},
|
||||
selectedDs: [],
|
||||
selectedApps: [],
|
||||
selectedResources: [],
|
||||
isAll: true,
|
||||
newPermissionName: null,
|
||||
},
|
||||
|
|
@ -86,15 +85,27 @@ class BaseManageGranularAccess extends React.Component {
|
|||
groupPermissionV2Service
|
||||
.fetchAddableApps()
|
||||
.then((data) => {
|
||||
const addableApps = data.map((app) => {
|
||||
return {
|
||||
name: app.name,
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
};
|
||||
});
|
||||
const addableApps = data
|
||||
.filter((app) => app.type === APP_TYPES.FRONT_END)
|
||||
.map((app) => {
|
||||
return {
|
||||
name: app.name,
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
};
|
||||
});
|
||||
const addableWorkflows = data
|
||||
.filter((app) => app.type === APP_TYPES.WORKFLOW)
|
||||
.map((app) => {
|
||||
return {
|
||||
name: app.name,
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
};
|
||||
});
|
||||
this.setState({
|
||||
addableApps,
|
||||
addableWorkflows,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
@ -137,19 +148,15 @@ class BaseManageGranularAccess extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
getSelectedResources = () => {
|
||||
return this.state.selectedResources;
|
||||
};
|
||||
|
||||
createGranularPermissions = () => {
|
||||
const {
|
||||
initialPermissionState,
|
||||
initialPermissionStateDs,
|
||||
isAll,
|
||||
newPermissionName,
|
||||
isCustom,
|
||||
selectedApps,
|
||||
selectedDs,
|
||||
resourceType,
|
||||
} = this.state;
|
||||
const type = resourceType === 'Apps' ? 'app' : 'data_source';
|
||||
const selectedResource = type == 'app' ? selectedApps : selectedDs;
|
||||
const { initialPermissionState, initialPermissionStateDs, isAll, newPermissionName, isCustom, resourceType } =
|
||||
this.state;
|
||||
const type = resourceType;
|
||||
const selectedResource = this.getSelectedResources();
|
||||
if (isCustom && selectedResource.length == 0) {
|
||||
toast.error('Please select the resources to continue');
|
||||
return;
|
||||
|
|
@ -157,7 +164,7 @@ class BaseManageGranularAccess extends React.Component {
|
|||
const resourcesToAdd = selectedResource
|
||||
.filter((res) => !res?.isAllField)
|
||||
.map((option) => {
|
||||
if (type === 'app') {
|
||||
if (type === RESOURCE_TYPE.APPS || type === RESOURCE_TYPE.WORKFLOWS) {
|
||||
return {
|
||||
appId: option.value,
|
||||
};
|
||||
|
|
@ -173,8 +180,8 @@ class BaseManageGranularAccess extends React.Component {
|
|||
groupId: this.props.groupPermissionId,
|
||||
isAll: isAll,
|
||||
createResourcePermissionObject: {
|
||||
...(type == 'app' && initialPermissionState),
|
||||
...(type == 'data_source' && { action: initialPermissionStateDs }),
|
||||
...((type === RESOURCE_TYPE.APPS || type === RESOURCE_TYPE.WORKFLOWS) && initialPermissionState),
|
||||
...(type == RESOURCE_TYPE.DATA_SOURCES && { action: initialPermissionStateDs }),
|
||||
resourcesToAdd: resourcesToAdd,
|
||||
},
|
||||
};
|
||||
|
|
@ -226,8 +233,8 @@ class BaseManageGranularAccess extends React.Component {
|
|||
canUse: dataSourcesGroupPermission?.canUse,
|
||||
canConfigure: dataSourcesGroupPermission?.canConfigure,
|
||||
},
|
||||
resourceType: 'Data sources',
|
||||
selectedDs:
|
||||
resourceType: RESOURCE_TYPE.DATA_SOURCES,
|
||||
selectedResources:
|
||||
currentDs?.length > 0
|
||||
? currentDs?.map(({ dataSource }) => {
|
||||
return {
|
||||
|
|
@ -238,14 +245,14 @@ class BaseManageGranularAccess extends React.Component {
|
|||
})
|
||||
: [],
|
||||
initialState: {
|
||||
type: 'data_source',
|
||||
type: RESOURCE_TYPE.DATA_SOURCES,
|
||||
initialPermissionStateDs: {
|
||||
canUse: dataSourcesGroupPermission?.canUse,
|
||||
canConfigure: dataSourcesGroupPermission?.canConfigure,
|
||||
},
|
||||
isAll: !!granularPermission.isAll,
|
||||
newPermissionName: granularPermission?.name,
|
||||
selectedDs:
|
||||
selectedResources:
|
||||
currentDs?.length > 0
|
||||
? currentDs?.map(({ dataSource }) => {
|
||||
return {
|
||||
|
|
@ -257,30 +264,32 @@ class BaseManageGranularAccess extends React.Component {
|
|||
: [],
|
||||
},
|
||||
});
|
||||
} else if (granularPermission.type === 'app') {
|
||||
} else if (granularPermission.type === RESOURCE_TYPE.APPS || granularPermission.type === RESOURCE_TYPE.WORKFLOWS) {
|
||||
const currentApps = granularPermission?.appsGroupPermissions?.groupApps;
|
||||
const appsGroupPermission = granularPermission?.appsGroupPermissions;
|
||||
const selectedResources =
|
||||
currentApps?.length > 0
|
||||
? currentApps?.map(({ app }) => {
|
||||
return {
|
||||
name: app.name,
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
this.setState({
|
||||
...fixedState,
|
||||
modalTitle: `Edit app permissions`,
|
||||
resourceType: 'Apps',
|
||||
modalTitle: `Edit ${granularPermission.type} permissions`,
|
||||
resourceType: granularPermission.type,
|
||||
initialPermissionState: {
|
||||
canEdit: appsGroupPermission.canEdit,
|
||||
canView: appsGroupPermission.canView,
|
||||
hideFromDashboard: appsGroupPermission.hideFromDashboard,
|
||||
},
|
||||
selectedApps:
|
||||
currentApps?.length > 0
|
||||
? currentApps?.map(({ app }) => {
|
||||
return {
|
||||
name: app.name,
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
};
|
||||
})
|
||||
: [],
|
||||
selectedResources: selectedResources,
|
||||
initialState: {
|
||||
type: 'app',
|
||||
type: granularPermission.type,
|
||||
initialPermissionState: {
|
||||
canEdit: appsGroupPermission?.canEdit,
|
||||
canView: appsGroupPermission?.canView,
|
||||
|
|
@ -288,21 +297,68 @@ class BaseManageGranularAccess extends React.Component {
|
|||
},
|
||||
isAll: !!granularPermission.isAll,
|
||||
newPermissionName: granularPermission?.name,
|
||||
selectedApps:
|
||||
currentApps?.length > 0
|
||||
? currentApps?.map(({ app }) => {
|
||||
return {
|
||||
name: app.name,
|
||||
value: app.id,
|
||||
label: app.name,
|
||||
};
|
||||
})
|
||||
: [],
|
||||
selectedResources: selectedResources,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderResourcePermissions = (props) => {
|
||||
const { permissions, currentGroupPermission, isBasicPlan, index } = props;
|
||||
const { type } = permissions;
|
||||
|
||||
switch (type) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return (
|
||||
<AppResourcePermissions
|
||||
updateOnlyGranularPermissions={this.updateOnlyGranularPermissions}
|
||||
permissions={permissions}
|
||||
currentGroupPermission={currentGroupPermission}
|
||||
openEditPermissionModal={this.openEditPermissionModal}
|
||||
isBasicPlan={isBasicPlan}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
return (
|
||||
<DataSourceResourcePermissions
|
||||
updateOnlyGranularPermissions={this.updateOnlyGranularPermissions}
|
||||
permissions={permissions}
|
||||
currentGroupPermission={currentGroupPermission}
|
||||
openEditPermissionModal={this.openEditPermissionModal}
|
||||
isBasicPlan={isBasicPlan}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return (
|
||||
<WorkflowResourcePermissions
|
||||
updateOnlyGranularPermissions={this.updateOnlyGranularPermissions}
|
||||
permissions={permissions}
|
||||
currentGroupPermission={currentGroupPermission}
|
||||
openEditPermissionModal={this.openEditPermissionModal}
|
||||
isBasicPlan={isBasicPlan}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
getAddableResources = (resourceType) => {
|
||||
switch (resourceType) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return this.state.addableApps;
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
return this.state.addableDs;
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return this.state.addableWorkflows;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
updateOnlyGranularPermissions = (permission, actions = {}, allowRoleChange) => {
|
||||
const body = {
|
||||
actions: actions,
|
||||
|
|
@ -340,32 +396,25 @@ class BaseManageGranularAccess extends React.Component {
|
|||
};
|
||||
|
||||
updateGranularPermissions = (allowRoleChange) => {
|
||||
const {
|
||||
currentEditingPermissions,
|
||||
selectedApps,
|
||||
selectedDs,
|
||||
newPermissionName,
|
||||
isAll,
|
||||
initialPermissionState,
|
||||
initialPermissionStateDs,
|
||||
} = this.state;
|
||||
const { currentEditingPermissions, newPermissionName, isAll, initialPermissionState, initialPermissionStateDs } =
|
||||
this.state;
|
||||
const type = currentEditingPermissions.type;
|
||||
const currentResource =
|
||||
type === 'app'
|
||||
type === RESOURCE_TYPE.APPS || type === RESOURCE_TYPE.WORKFLOWS
|
||||
? currentEditingPermissions?.appsGroupPermissions?.groupApps?.map((app) => {
|
||||
return app.app.id;
|
||||
})
|
||||
: currentEditingPermissions?.dataSourcesGroupPermission?.groupDataSources?.map((ds) => {
|
||||
return ds.dataSource.id;
|
||||
});
|
||||
const selectedResourceEnitities = type === 'app' ? selectedApps : selectedDs;
|
||||
const selectedResourceEnitities = this.getSelectedResources();
|
||||
const selectedResource = selectedResourceEnitities
|
||||
.filter((res) => !res?.isAllField)
|
||||
?.map((resource) => resource.value);
|
||||
const resourcesToAdd = selectedResource
|
||||
?.filter((item) => !currentResource.includes(item))
|
||||
.map((id) => {
|
||||
if (type === 'app')
|
||||
if (type === RESOURCE_TYPE.APPS || type === RESOURCE_TYPE.WORKFLOWS)
|
||||
return {
|
||||
appId: id,
|
||||
};
|
||||
|
|
@ -377,7 +426,7 @@ class BaseManageGranularAccess extends React.Component {
|
|||
});
|
||||
const resourceItemsToDelete = currentResource?.filter((item) => !selectedResource?.includes(item));
|
||||
const groupResToDelete =
|
||||
type === 'app'
|
||||
type === RESOURCE_TYPE.APPS || type === RESOURCE_TYPE.WORKFLOWS
|
||||
? currentEditingPermissions?.appsGroupPermissions?.groupApps?.filter((groupApp) =>
|
||||
resourceItemsToDelete?.includes(groupApp.appId)
|
||||
)
|
||||
|
|
@ -392,7 +441,10 @@ class BaseManageGranularAccess extends React.Component {
|
|||
const body = {
|
||||
name: newPermissionName,
|
||||
isAll: isAll,
|
||||
actions: type === 'app' ? initialPermissionState : initialPermissionStateDs,
|
||||
actions:
|
||||
type === RESOURCE_TYPE.APPS || type === RESOURCE_TYPE.WORKFLOWS
|
||||
? initialPermissionState
|
||||
: initialPermissionStateDs,
|
||||
resourcesToAdd,
|
||||
resourcesToDelete,
|
||||
allowRoleChange,
|
||||
|
|
@ -457,7 +509,7 @@ class BaseManageGranularAccess extends React.Component {
|
|||
|
||||
openAddPermissionModal = (resourceType) => {
|
||||
this.setState((prevState) => ({
|
||||
modalTitle: `Add ${resourceType?.toLowerCase()} permissions`,
|
||||
modalTitle: `Add ${RESOURCE_NAME_MAPPING[resourceType].toLowerCase()} permissions`,
|
||||
resourceType,
|
||||
showAddPermissionModal: true,
|
||||
initialPermissionState: { ...prevState.initialPermissionState, canView: true },
|
||||
|
|
@ -484,22 +536,14 @@ class BaseManageGranularAccess extends React.Component {
|
|||
canUse: false,
|
||||
canConfigure: false,
|
||||
},
|
||||
selectedDs: [],
|
||||
selectedApps: [],
|
||||
selectedResources: [],
|
||||
resourceType: '',
|
||||
hasChanges: false,
|
||||
});
|
||||
};
|
||||
|
||||
setSelectedApps = (values) => {
|
||||
this.setState({ selectedApps: values }, () => {
|
||||
const hasChanges = this.hasStateChanged(this.state);
|
||||
this.setState({ hasChanges });
|
||||
});
|
||||
};
|
||||
|
||||
setSelectedDs = (values) => {
|
||||
this.setState({ selectedDs: values }, () => {
|
||||
setSelectedResources = (values) => {
|
||||
this.setState({ selectedResources: values }, () => {
|
||||
const hasChanges = this.hasStateChanged(this.state);
|
||||
this.setState({ hasChanges });
|
||||
});
|
||||
|
|
@ -525,15 +569,13 @@ class BaseManageGranularAccess extends React.Component {
|
|||
hasStateChanged = (newState) => {
|
||||
const { type } = this.state.initialState;
|
||||
|
||||
const selectedItems =
|
||||
type === 'data_source' ? this.state.initialState?.selectedDs : this.state.initialState?.selectedApps;
|
||||
|
||||
const newSelectedItems = type === 'data_source' ? newState.selectedDs : newState.selectedApps;
|
||||
const selectedItems = this.state.initialState?.selectedResources;
|
||||
const newSelectedItems = newState.selectedResources;
|
||||
const newPermissionState =
|
||||
type === 'data_source' ? newState.initialPermissionStateDs : newState.initialPermissionState;
|
||||
type === RESOURCE_TYPE.DATA_SOURCES ? newState.initialPermissionStateDs : newState.initialPermissionState;
|
||||
|
||||
const permissionStateChanged =
|
||||
type === 'data_source'
|
||||
type === RESOURCE_TYPE.DATA_SOURCES
|
||||
? this.state.initialState.initialPermissionStateDs?.canUse !== newPermissionState?.canUse ||
|
||||
this.state.initialPermissionStateDs?.canConfigure !== newPermissionState?.canConfigure
|
||||
: this.state.initialState.initialPermissionState?.canEdit !== newPermissionState?.canEdit ||
|
||||
|
|
@ -574,11 +616,9 @@ class BaseManageGranularAccess extends React.Component {
|
|||
render() {
|
||||
const {
|
||||
showAddPermissionModal,
|
||||
selectedApps,
|
||||
isCustom,
|
||||
granularPermissions,
|
||||
isLoading,
|
||||
addableApps,
|
||||
modalTitle,
|
||||
modalType,
|
||||
newPermissionName,
|
||||
|
|
@ -589,7 +629,6 @@ class BaseManageGranularAccess extends React.Component {
|
|||
deleteConfirmationModal,
|
||||
deletingPermissions,
|
||||
resourceType,
|
||||
selectedDs,
|
||||
hasChanges,
|
||||
} = this.state;
|
||||
|
||||
|
|
@ -601,7 +640,7 @@ class BaseManageGranularAccess extends React.Component {
|
|||
const showPermissionInfo = currentGroupPermission.name == 'admin' || currentGroupPermission.name == 'end-user';
|
||||
const addPermissionTooltipMessage = !newPermissionName
|
||||
? 'Please input permissions name'
|
||||
: isCustom && selectedApps.length === 0
|
||||
: isCustom && this.getSelectedResources().length === 0
|
||||
? 'Please select apps or select all apps option'
|
||||
: '';
|
||||
const isBasicPlan = this.props.isBasicPlan;
|
||||
|
|
@ -632,63 +671,73 @@ class BaseManageGranularAccess extends React.Component {
|
|||
darkMode={this.props.darkMode}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<AddEditResourcePermissionsModal
|
||||
handleClose={this.closeAddPermissionModal}
|
||||
handleConfirm={
|
||||
modalType === 'add'
|
||||
? this.createGranularPermissions
|
||||
: () => {
|
||||
this.updateGranularPermissions();
|
||||
}
|
||||
}
|
||||
updateParentState={this.updateState}
|
||||
resourceType={resourceType}
|
||||
currentState={this.state}
|
||||
show={showAddPermissionModal}
|
||||
title={
|
||||
<div className="my-3 permission-manager-title" data-cy="modal-title">
|
||||
<span className="font-weight-500">
|
||||
<SolidIcon name={resourceType == 'Apps' ? 'apps' : 'datasource'} fill="var(--slate8)" />
|
||||
</span>
|
||||
<div className="tj-text-md font-weight-500 modal-name" data-cy="modal-title">
|
||||
{modalTitle}
|
||||
</div>
|
||||
{modalType === 'edit' && !isRoleGroup && (
|
||||
<div className="delete-icon-cont">
|
||||
<ButtonSolid
|
||||
leftIcon="delete"
|
||||
iconWidth="15px"
|
||||
className="icon-class"
|
||||
variant="tertiary"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
deleteConfirmationModal: true,
|
||||
showAddPermissionModal: false,
|
||||
});
|
||||
}}
|
||||
data-cy="delete-button"
|
||||
{showAddPermissionModal && (
|
||||
<AddEditResourcePermissionsModal
|
||||
handleClose={this.closeAddPermissionModal}
|
||||
handleConfirm={
|
||||
modalType === 'add'
|
||||
? this.createGranularPermissions
|
||||
: () => {
|
||||
this.updateGranularPermissions();
|
||||
}
|
||||
}
|
||||
updateParentState={this.updateState}
|
||||
resourceType={resourceType}
|
||||
currentState={this.state}
|
||||
show={showAddPermissionModal}
|
||||
title={
|
||||
<div className="my-3 permission-manager-title" data-cy="modal-title">
|
||||
<span className="font-weight-500">
|
||||
<SolidIcon
|
||||
name={
|
||||
resourceType === RESOURCE_TYPE.APPS
|
||||
? 'apps'
|
||||
: resourceType === RESOURCE_TYPE.WORKFLOWS
|
||||
? 'workflows'
|
||||
: 'datasource'
|
||||
}
|
||||
fill="var(--slate8)"
|
||||
/>
|
||||
</span>
|
||||
<div className="tj-text-md font-weight-500 modal-name" data-cy="modal-title">
|
||||
{modalTitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
confirmBtnProps={{
|
||||
title: `${modalType === 'edit' ? 'Update' : 'Add'}`,
|
||||
iconLeft: 'plus',
|
||||
disabled:
|
||||
(modalType === 'add' && !newPermissionName) ||
|
||||
(modalType === 'edit' && !hasChanges) ||
|
||||
(isCustom && selectedApps.length === 0 && resourceType === 'Apps') ||
|
||||
(isCustom && selectedDs.length === 0 && resourceType === 'Data Sources'),
|
||||
tooltipMessage: addPermissionTooltipMessage,
|
||||
}}
|
||||
disableBuilderLevelUpdate={disableEditUpdate}
|
||||
selectedApps={resourceType === 'Apps' ? selectedApps : selectedDs}
|
||||
setSelectedApps={resourceType === 'Apps' ? this.setSelectedApps : this.setSelectedDs}
|
||||
addableApps={resourceType === 'Apps' ? addableApps : addableDs}
|
||||
darkMode={this.props.darkMode}
|
||||
groupName={currentGroupPermission.name}
|
||||
/>
|
||||
{modalType === 'edit' && !isRoleGroup && (
|
||||
<div className="delete-icon-cont">
|
||||
<ButtonSolid
|
||||
leftIcon="delete"
|
||||
iconWidth="15px"
|
||||
className="icon-class"
|
||||
variant="tertiary"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
deleteConfirmationModal: true,
|
||||
showAddPermissionModal: false,
|
||||
});
|
||||
}}
|
||||
data-cy="delete-button"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
confirmBtnProps={{
|
||||
title: `${modalType === 'edit' ? 'Update' : 'Add'}`,
|
||||
iconLeft: 'plus',
|
||||
disabled:
|
||||
(modalType === 'add' && !newPermissionName) ||
|
||||
(modalType === 'edit' && !hasChanges) ||
|
||||
(isCustom && this.getSelectedResources().length === 0),
|
||||
tooltipMessage: addPermissionTooltipMessage,
|
||||
}}
|
||||
disableBuilderLevelUpdate={disableEditUpdate}
|
||||
selectedApps={this.getSelectedResources()}
|
||||
setSelectedApps={(values) => this.setSelectedResources(values)}
|
||||
addableApps={this.getAddableResources(resourceType)}
|
||||
darkMode={this.props.darkMode}
|
||||
groupName={currentGroupPermission.name}
|
||||
/>
|
||||
)}
|
||||
{!granularPermissions.length && !isLoading ? (
|
||||
<div className="empty-container">
|
||||
<div className="icon-container" data-cy="empty-page-svg">
|
||||
|
|
@ -735,28 +784,12 @@ class BaseManageGranularAccess extends React.Component {
|
|||
) : (
|
||||
<>
|
||||
{granularPermissions.map((permissions, index) => {
|
||||
if (permissions.type === 'app')
|
||||
return (
|
||||
<AppResourcePermissions
|
||||
updateOnlyGranularPermissions={this.updateOnlyGranularPermissions}
|
||||
permissions={permissions}
|
||||
currentGroupPermission={currentGroupPermission}
|
||||
openEditPermissionModal={this.openEditPermissionModal}
|
||||
isBasicPlan={isBasicPlan}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
else
|
||||
return (
|
||||
<DataSourceResourcePermissions
|
||||
updateOnlyGranularPermissions={this.updateOnlyGranularPermissions}
|
||||
permissions={permissions}
|
||||
currentGroupPermission={currentGroupPermission}
|
||||
openEditPermissionModal={this.openEditPermissionModal}
|
||||
isBasicPlan={isBasicPlan}
|
||||
key={index}
|
||||
/>
|
||||
);
|
||||
return this.renderResourcePermissions({
|
||||
permissions,
|
||||
currentGroupPermission,
|
||||
isBasicPlan,
|
||||
index,
|
||||
});
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { AppsSelect } from '@/_ui/Modal/AppsSelect';
|
|||
import AppPermissionsActions from './AppPermissionActionContainer';
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
import DsPermissionsActions from './DataSourcPermissionActionContainer';
|
||||
import WorkflowPermissionsActions from './WorkflowPermissionActionContainer';
|
||||
import { RESOURCE_TYPE } from '../../../../index';
|
||||
|
||||
function AddEditResourcePermissionsModal({
|
||||
handleClose,
|
||||
|
|
@ -28,11 +30,138 @@ function AddEditResourcePermissionsModal({
|
|||
const initialPermissionStateDs = currentState?.initialPermissionStateDs;
|
||||
const errors = currentState?.errors;
|
||||
const isAll = currentState?.isAll;
|
||||
const allResourceText =
|
||||
resourceType === 'Apps'
|
||||
? 'This will select all apps in the workspace including any new apps created'
|
||||
: 'This will select all data sources in the workspace including any new connections created';
|
||||
const allResourceTitle = resourceType === 'Apps' ? 'All apps' : 'All data sources';
|
||||
const getAllResourceText = (resourceType) => {
|
||||
switch (resourceType) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return 'This will select all apps in the workspace including any new apps created';
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return 'This will select all workflows in the workspace including any new workflows created';
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
return 'This will select all data sources in the workspace including any new connections created';
|
||||
}
|
||||
};
|
||||
|
||||
const RESOURCE_NAME_MAPPING = {
|
||||
[RESOURCE_TYPE.APPS]: 'apps',
|
||||
[RESOURCE_TYPE.WORKFLOWS]: 'workflows',
|
||||
[RESOURCE_TYPE.DATA_SOURCES]: 'data sources',
|
||||
};
|
||||
|
||||
const getAllResourceLabel = (resourceType) => {
|
||||
switch (resourceType) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return 'All apps';
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return 'All workflows';
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
return 'All data sources';
|
||||
default:
|
||||
return 'All resources';
|
||||
}
|
||||
};
|
||||
|
||||
const renderPermissionActions = (resourceType) => {
|
||||
switch (resourceType) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return (
|
||||
<AppPermissionsActions
|
||||
handleClickEdit={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
canEdit: !prevState.initialPermissionState.canEdit,
|
||||
canView: prevState.initialPermissionState.canEdit,
|
||||
...(!prevState.initialPermissionState.canEdit && { hideFromDashboard: false }),
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleClickView={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
canView: !prevState.initialPermissionState.canView,
|
||||
canEdit: prevState.initialPermissionState.canView,
|
||||
...(prevState.initialPermissionState.canEdit && { hideFromDashboard: false }),
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleHideFromDashboard={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
hideFromDashboard: !prevState.initialPermissionState.hideFromDashboard,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
disableBuilderLevelUpdate={disableBuilderLevelUpdate}
|
||||
initialPermissionState={initialPermissionState}
|
||||
/>
|
||||
);
|
||||
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return (
|
||||
<WorkflowPermissionsActions
|
||||
handleClickEdit={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
canEdit: !prevState.initialPermissionState.canEdit,
|
||||
canView: prevState.initialPermissionState.canEdit,
|
||||
...(!prevState.initialPermissionState.canEdit && { hideFromDashboard: false }),
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleClickView={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
canView: !prevState.initialPermissionState.canView,
|
||||
canEdit: prevState.initialPermissionState.canView,
|
||||
...(prevState.initialPermissionState.canEdit && { hideFromDashboard: false }),
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleHideFromDashboard={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
hideFromDashboard: !prevState.initialPermissionState.hideFromDashboard,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
disableBuilderLevelUpdate={disableBuilderLevelUpdate}
|
||||
initialPermissionState={initialPermissionState}
|
||||
/>
|
||||
);
|
||||
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
default:
|
||||
return (
|
||||
<DsPermissionsActions
|
||||
handleClickConfigure={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionStateDs: {
|
||||
...prevState.initialPermissionStateDs,
|
||||
canConfigure: !prevState.initialPermissionStateDs.canConfigure,
|
||||
canUse: !!prevState.initialPermissionStateDs.canConfigure,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleClickUse={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionStateDs: {
|
||||
...prevState.initialPermissionStateDs,
|
||||
canUse: !prevState.initialPermissionStateDs.canUse,
|
||||
canConfigure: !!prevState.initialPermissionStateDs.canUse,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
disableBuilderLevelUpdate={disableBuilderLevelUpdate}
|
||||
initialPermissionStateDs={initialPermissionStateDs}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ModalBase
|
||||
size="md"
|
||||
|
|
@ -79,46 +208,7 @@ function AddEditResourcePermissionsModal({
|
|||
<label className="form-label bold-text" data-cy="permission-label">
|
||||
Permission
|
||||
</label>
|
||||
{resourceType === 'Apps' ? (
|
||||
<AppPermissionsActions
|
||||
handleClickEdit={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
canEdit: !prevState.initialPermissionState.canEdit,
|
||||
canView: prevState.initialPermissionState.canEdit,
|
||||
...(!prevState.initialPermissionState.canEdit && { hideFromDashboard: false }),
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleClickView={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...prevState.initialPermissionState,
|
||||
canView: !prevState.initialPermissionState.canView,
|
||||
canEdit: prevState.initialPermissionState.canView,
|
||||
...(prevState.initialPermissionState.canEdit && { hideFromDashboard: false }),
|
||||
},
|
||||
}));
|
||||
}}
|
||||
handleHideFromDashboard={() => {
|
||||
updateParentState((prevState) => ({
|
||||
initialPermissionState: {
|
||||
...initialPermissionState,
|
||||
hideFromDashboard: !prevState.initialPermissionState.hideFromDashboard,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
disableBuilderLevelUpdate={disableBuilderLevelUpdate}
|
||||
initialPermissionState={initialPermissionState}
|
||||
/>
|
||||
) : (
|
||||
<DsPermissionsActions
|
||||
updateParentState={updateParentState}
|
||||
disableBuilderLevelUpdate={disableBuilderLevelUpdate}
|
||||
initialPermissionStateDs={initialPermissionStateDs}
|
||||
/>
|
||||
)}
|
||||
{renderPermissionActions(resourceType)}
|
||||
</div>
|
||||
|
||||
<div className="form-group mb-3">
|
||||
|
|
@ -139,15 +229,15 @@ function AddEditResourcePermissionsModal({
|
|||
<div>
|
||||
<span
|
||||
className="form-check-label"
|
||||
data-cy={`${allResourceTitle.toLowerCase().replace(/\s+/g, '-')}-label`}
|
||||
data-cy={`${getAllResourceLabel(resourceType).toLowerCase().replace(/\s+/g, '-')}-label`}
|
||||
>
|
||||
{allResourceTitle}
|
||||
{getAllResourceLabel(resourceType)}
|
||||
</span>
|
||||
<span
|
||||
className="tj-text-xsm"
|
||||
data-cy={`${allResourceTitle.toLowerCase().replace(/\s+/g, '-')}-info-text`}
|
||||
data-cy={`${getAllResourceText(resourceType).toLowerCase().replace(/\s+/g, '-')}-info-text`}
|
||||
>
|
||||
{allResourceText}
|
||||
{getAllResourceText(resourceType)}
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
|
@ -167,7 +257,9 @@ function AddEditResourcePermissionsModal({
|
|||
<input
|
||||
className="form-check-input"
|
||||
type="radio"
|
||||
disabled={addableApps.length === 0 || disableBuilderLevelUpdate || groupName === 'builder'}
|
||||
disabled={
|
||||
!addableApps || addableApps?.length === 0 || disableBuilderLevelUpdate || groupName === 'builder'
|
||||
}
|
||||
checked={isCustom}
|
||||
onClick={() => {
|
||||
!isCustom &&
|
||||
|
|
@ -188,8 +280,7 @@ function AddEditResourcePermissionsModal({
|
|||
style={{ color: disableBuilderLevelUpdate ? 'var(--text-disabled)' : '' }}
|
||||
data-cy="custom-info-text"
|
||||
>
|
||||
Select specific {resourceType === 'Apps' ? 'applications' : 'data sources'} you want to add to the
|
||||
group
|
||||
Select specific {RESOURCE_NAME_MAPPING[resourceType]} you want to add to the group
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import React from 'react';
|
||||
import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
|
||||
|
||||
function WorkflowPermissionActionContainer() {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export default withEditionSpecificComponent(WorkflowPermissionActionContainer, 'WorkspaceSettings');
|
||||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import '../../../resources/styles/group-permissions.styles.scss';
|
||||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
import { RESOURCE_TYPE } from '../../../index';
|
||||
|
||||
function AddResourcePermissionsMenu({
|
||||
openAddPermissionModal,
|
||||
|
|
@ -10,6 +11,25 @@ function AddResourcePermissionsMenu({
|
|||
darkMode,
|
||||
isBasicPlan,
|
||||
}) {
|
||||
const selectResourceIcon = (resourceType) => {
|
||||
switch (resourceType) {
|
||||
case RESOURCE_TYPE.APPS:
|
||||
return 'apps';
|
||||
case RESOURCE_TYPE.WORKFLOWS:
|
||||
return 'workflows';
|
||||
case RESOURCE_TYPE.DATA_SOURCES:
|
||||
return 'datasource';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const resourceNameMapping = {
|
||||
[RESOURCE_TYPE.APPS]: 'Apps',
|
||||
[RESOURCE_TYPE.WORKFLOWS]: 'Workflows',
|
||||
[RESOURCE_TYPE.DATA_SOURCES]: 'Data source',
|
||||
};
|
||||
|
||||
return resourcesOptions.length > 1 ? (
|
||||
<OverlayTrigger
|
||||
rootClose={true}
|
||||
|
|
@ -25,17 +45,17 @@ function AddResourcePermissionsMenu({
|
|||
iconWidth="17"
|
||||
fill="var(--slate9)"
|
||||
className="apps-remove-btn permission-type remove-decoration tj-text-xsm font-weight-600 remove-disabled-bg"
|
||||
leftIcon={resource === 'Apps' ? 'apps' : 'datasource'}
|
||||
leftIcon={selectResourceIcon(resource)}
|
||||
onClick={() => {
|
||||
openAddPermissionModal(resource);
|
||||
}}
|
||||
disabled={currentGroupPermission.name === 'end-user' && resource === 'Data Sources'}
|
||||
disabled={currentGroupPermission.name === 'end-user' && resource === RESOURCE_TYPE.DATA_SOURCES}
|
||||
>
|
||||
<OverlayTrigger
|
||||
key={index}
|
||||
placement="right"
|
||||
overlay={
|
||||
currentGroupPermission.name === 'end-user' && resource === 'Data Sources' ? (
|
||||
currentGroupPermission.name === 'end-user' && resource === RESOURCE_TYPE.DATA_SOURCES ? (
|
||||
<Tooltip id={`tooltip-${index}`} style={{ maxWidth: '120px' }}>
|
||||
End-user cannot access data sources
|
||||
</Tooltip>
|
||||
|
|
@ -44,7 +64,7 @@ function AddResourcePermissionsMenu({
|
|||
)
|
||||
}
|
||||
>
|
||||
<span>{resource === 'Data Sources' ? 'Data source' : resource}</span>
|
||||
<span>{resourceNameMapping[resource]}</span>
|
||||
</OverlayTrigger>
|
||||
</ButtonSolid>
|
||||
))}
|
||||
|
|
@ -75,7 +95,7 @@ function AddResourcePermissionsMenu({
|
|||
leftIcon="plus"
|
||||
disabled={currentGroupPermission.name === 'admin' || isBasicPlan}
|
||||
onClick={() => {
|
||||
openAddPermissionModal('Apps');
|
||||
openAddPermissionModal(RESOURCE_TYPE.APPS);
|
||||
}}
|
||||
data-cy="add-apps-buton"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import React from 'react';
|
||||
import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
|
||||
|
||||
function WorkflowResourcePermissions() {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export default withEditionSpecificComponent(WorkflowResourcePermissions, 'WorkspaceSettings');
|
||||
|
|
@ -21,6 +21,7 @@ import ChangeRoleModal from '../ChangeRoleModal';
|
|||
import { ToolTip } from '@/_components/ToolTip';
|
||||
import Avatar from '@/_ui/Avatar';
|
||||
import DataSourcePermissionsUI from '../DataSourcePermissionsUI';
|
||||
import WorkflowPermissionsUI from '../WorkflowPermissionsUI';
|
||||
|
||||
class BaseManageGroupPermissionResources extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -876,7 +877,7 @@ class BaseManageGroupPermissionResources extends React.Component {
|
|||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="permission-body">
|
||||
<div className={`${showPermissionInfo ? 'permissions-body-one' : 'permissions-body-two'}`}>
|
||||
{isLoadingGroup ? (
|
||||
<tr>
|
||||
<td className="col-auto">
|
||||
|
|
@ -958,6 +959,13 @@ class BaseManageGroupPermissionResources extends React.Component {
|
|||
</div>
|
||||
{/* //App till here */}
|
||||
</div>
|
||||
{/* Worklfow Permission */}
|
||||
<WorkflowPermissionsUI
|
||||
groupPermission={groupPermission}
|
||||
disablePermissionUpdate={disablePermissionUpdate}
|
||||
updateGroupPermission={this.updateGroupPermission}
|
||||
updateState={this.updateParamState}
|
||||
/>
|
||||
|
||||
{/* Data source */}
|
||||
<DataSourcePermissionsUI
|
||||
|
|
|
|||
|
|
@ -141,7 +141,21 @@
|
|||
}
|
||||
|
||||
|
||||
.permission-body {
|
||||
.permissions-body-one {
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 320px - 100px);
|
||||
min-height: calc(100vh - 320px - 100px);
|
||||
|
||||
.tj-text-xxsm {
|
||||
color: var(--slate11)
|
||||
}
|
||||
}
|
||||
|
||||
.permissions-body-two {
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 260px - 100px);
|
||||
min-height: calc(100vh - 260px - 100px);
|
||||
|
||||
.tj-text-xxsm {
|
||||
color: var(--slate11)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -827,6 +827,7 @@ class BaseManageGroupPermissions extends React.Component {
|
|||
value: group.name,
|
||||
};
|
||||
})}
|
||||
workflowEnabled={false}
|
||||
featureAccess={featureAccess}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import React from 'react';
|
||||
import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
|
||||
|
||||
const WorkflowPermissionsUI = () => {
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default withEditionSpecificComponent(WorkflowPermissionsUI, 'WorkspaceSettings');
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './WorkflowPermissionsUI.jsx';
|
||||
|
|
@ -1 +1,18 @@
|
|||
export { default } from './ManageGroupPermissionsPage';
|
||||
|
||||
export const RESOURCE_TYPE = {
|
||||
APPS: 'app',
|
||||
DATA_SOURCES: 'data_source',
|
||||
WORKFLOWS: 'workflow',
|
||||
};
|
||||
|
||||
export const APP_TYPES = {
|
||||
FRONT_END: 'front-end',
|
||||
WORKFLOW: 'workflow',
|
||||
};
|
||||
|
||||
export const RESOURCE_NAME_MAPPING = {
|
||||
[RESOURCE_TYPE.APPS]: 'Apps',
|
||||
[RESOURCE_TYPE.DATA_SOURCES]: 'Data Sources',
|
||||
[RESOURCE_TYPE.WORKFLOWS]: 'Workflows',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -303,18 +303,17 @@
|
|||
.manage-resource-permission {
|
||||
|
||||
|
||||
.tj-text-xxsm {
|
||||
color: var(--slate11)
|
||||
}
|
||||
|
||||
|
||||
transition: background-color 0.3s ease;
|
||||
border-bottom: 1px solid var(--slate5);
|
||||
display: flex;
|
||||
align-items:flex-start;
|
||||
align-items: flex-start;
|
||||
padding: 12px;
|
||||
gap: 10px;
|
||||
|
||||
.tj-text-xxsm {
|
||||
color: var(--slate11)
|
||||
}
|
||||
|
||||
.resource-name {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
const BaseComponentModuleTab = () => {
|
||||
return <p className="widgets-manager-header">Components</p>;
|
||||
};
|
||||
|
||||
export default BaseComponentModuleTab;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './BaseComponentModuleTab';
|
||||
|
|
@ -180,7 +180,7 @@ export const CreateOrganization = ({ showCreateOrg, setShowCreateOrg }) => {
|
|||
// this is to denote that the user has tried editing the slug -- so now slug and name are independent of each other
|
||||
isSlugSet.current = true;
|
||||
}
|
||||
}, [name.value, slug.value, slugProgress, workspaceNameProgress, isSlugSet]);
|
||||
}, [name.value, slugProgress, workspaceNameProgress, isSlugSet]);
|
||||
|
||||
const isDisabled =
|
||||
isCreating ||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import BaseOnboardingQuestions from './BaseOnboardingQuestions';
|
|||
import BaseSetupAdminPage from './BaseSetupAdminPage';
|
||||
import UsersTable from './UsersTable';
|
||||
import BaseColorSwatches from './BaseColorSwatches';
|
||||
import BaseComponentModuleTab from './BaseComponentModuleTab';
|
||||
|
||||
export {
|
||||
FormTextInput,
|
||||
|
|
@ -56,4 +57,5 @@ export {
|
|||
BaseOnboardingQuestions,
|
||||
BaseSetupAdminPage,
|
||||
UsersTable,
|
||||
BaseComponentModuleTab,
|
||||
};
|
||||
|
|
|
|||
2222
package-lock.json
generated
2222
package-lock.json
generated
File diff suppressed because it is too large
Load diff
28
package.json
28
package.json
|
|
@ -3,8 +3,8 @@
|
|||
"version": "1.18.0",
|
||||
"description": "ToolJet is an open-source low-code framework to build and deploy internal tools.",
|
||||
"engines": {
|
||||
"node": "18.18.2",
|
||||
"npm": "9.8.1"
|
||||
"node": "22.15.1",
|
||||
"npm": "10.9.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./frontend/src/**/*.{js,jsx}": [
|
||||
|
|
@ -19,11 +19,11 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@tooljet/cli": "^0.0.13",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-jest": "^28.6.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.1.2"
|
||||
"eslint": "^9.28.0",
|
||||
"eslint-plugin-jest": "^28.13.3",
|
||||
"eslint-plugin-prettier": "^5.4.1",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild:plugins": "npm run install:plugins",
|
||||
|
|
@ -40,6 +40,8 @@
|
|||
"postbuild:server": "npm --prefix server prune --production",
|
||||
"build": "npm run build:plugins:prod && npm run build:frontend && npm run build:server",
|
||||
"start:prod": "npm --prefix server run start:prod",
|
||||
"cloud:setup": "npm run db:setup && npm run plugins:install && npm run plugins:uninstall && npm run plugins:reload",
|
||||
"cloud:setup:prod": "npm run db:setup:prod && npm run plugins:install:prod && npm run plugins:uninstall:prod && npm run plugins:reload:prod",
|
||||
"db:create": "npm --prefix server run db:create",
|
||||
"db:create:prod": "npm --prefix server run db:create:prod",
|
||||
"db:migrate": "npm --prefix server run db:migrate",
|
||||
|
|
@ -51,12 +53,18 @@
|
|||
"db:reset": "npm --prefix server run db:reset",
|
||||
"db:drop": "npm --prefix server run db:drop",
|
||||
"deploy": "cp -a frontend/build/. public/",
|
||||
"plugins:install": "npm --prefix server run plugins:install",
|
||||
"plugins:install:prod": "npm --prefix server run plugins:install:prod",
|
||||
"plugins:uninstall": "npm --prefix server run plugins:uninstall",
|
||||
"plugins:uninstall:prod": "npm --prefix server run plugins:uninstall:prod",
|
||||
"plugins:reload": "npm --prefix server run plugins:reload",
|
||||
"plugins:reload:prod": "npm --prefix server run plugins:reload:prod",
|
||||
"heroku-postbuild": "./heroku-postbuild.sh",
|
||||
"prepare": "husky install",
|
||||
"update-version": "node update-version.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.12.0",
|
||||
"eslint-config-prettier": "^9.1.0"
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
||||
"eslint-config-prettier": "^10.1.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
3.14.0
|
||||
3.15.1
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ function buildConnectionOptions(): TypeOrmModuleOptions {
|
|||
migrationsTransactionMode: 'all',
|
||||
logging: data.ORM_LOGGING || false,
|
||||
migrations: [__dirname + '/data-migrations/**/*{.ts,.js}'],
|
||||
keepConnectionAlive: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
55
server/data-migrations/1742215016773-InsertDataGithubSSH.ts
Normal file
55
server/data-migrations/1742215016773-InsertDataGithubSSH.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class InsertDataGithubSSH1742215016773 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
INSERT INTO organization_git_ssh (git_url, ssh_private_key, ssh_public_key, key_type, config_id, is_finalized)
|
||||
SELECT git_url, ssh_private_key, ssh_public_key, key_type, id , is_finalized FROM organization_git_sync;
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE organization_git_sync
|
||||
DROP COLUMN IF EXISTS git_url,
|
||||
DROP COLUMN IF EXISTS ssh_private_key,
|
||||
DROP COLUMN IF EXISTS ssh_public_key,
|
||||
DROP COLUMN IF EXISTS key_type,
|
||||
DROP COLUMN IF EXISTS is_finalized;
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// Add the columns back to the old table
|
||||
await queryRunner.query(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_type WHERE typname = 'ssh_key_type'
|
||||
) THEN
|
||||
CREATE TYPE ssh_key_type AS ENUM (
|
||||
'rsa',
|
||||
'ed25519'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE organization_git_sync
|
||||
ADD COLUMN git_url VARCHAR(255),
|
||||
ADD COLUMN ssh_private_key TEXT,
|
||||
ADD COLUMN ssh_public_key TEXT,
|
||||
ADD COLUMN key_type ssh_key_type DEFAULT 'ed25519',
|
||||
ADD COLUMN is_finalized BOOLEAN DEFAULT false;
|
||||
`);
|
||||
|
||||
// Copy the data back from the new table to the old table
|
||||
await queryRunner.query(`
|
||||
UPDATE organization_git_sync
|
||||
SET git_url = s.git_url,
|
||||
ssh_private_key = s.ssh_private_key,
|
||||
ssh_public_key = s.ssh_public_key,
|
||||
key_type = s.key_type,
|
||||
is_finalized = s.is_finalized,
|
||||
FROM organization_git_ssh s
|
||||
WHERE organization_git_sync.id = s.config_id;
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateGlobalDataSources1743058812003 implements MigrationInterface {
|
||||
export class UpdateGlobalDataSources1742888566919 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Step 1: Set app_version_id to NULL for existing global data sources
|
||||
await queryRunner.query(`
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class DeprecateLocalStaticDataSourcesb1745318714733 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Get all organization IDs
|
||||
const organizationsResult = await queryRunner.query(`SELECT id FROM organizations`);
|
||||
const organizationIds = organizationsResult.map((row) => row.id);
|
||||
|
||||
console.log(`Found ${organizationIds.length} organizations to process`);
|
||||
|
||||
// Process each organization
|
||||
for (const orgId of organizationIds) {
|
||||
console.log(`Processing organization: ${orgId}`);
|
||||
|
||||
// Get all app_version_ids under this organization
|
||||
const appVersionsResult = await queryRunner.query(
|
||||
`
|
||||
SELECT av.id as app_version_id
|
||||
FROM app_versions av
|
||||
JOIN apps a ON av.app_id = a.id
|
||||
WHERE a.organization_id = $1
|
||||
`,
|
||||
[orgId]
|
||||
);
|
||||
|
||||
const appVersionIds = appVersionsResult.map((row) => row.app_version_id);
|
||||
|
||||
if (appVersionIds.length === 0) {
|
||||
console.log(`No app versions found for organization: ${orgId}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Found ${appVersionIds.length} app versions for organization: ${orgId}`);
|
||||
|
||||
// Get all distinct kinds for static global data sources associated with these app versions
|
||||
const kindsResult = await queryRunner.query(
|
||||
`
|
||||
SELECT DISTINCT kind
|
||||
FROM data_sources
|
||||
WHERE type = 'static' AND scope = 'local' AND app_version_id = ANY($1::uuid[])
|
||||
`,
|
||||
[appVersionIds]
|
||||
);
|
||||
|
||||
const kinds = kindsResult.map((row) => row.kind);
|
||||
|
||||
if (kinds.length === 0) {
|
||||
console.log(`No global static data sources found for app versions under organization: ${orgId}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Found ${kinds.length} different kinds of data sources`);
|
||||
|
||||
// Process each kind of data source
|
||||
for (const kind of kinds) {
|
||||
console.log(`Processing kind: ${kind}`);
|
||||
|
||||
// Get first data source ID of this kind to keep
|
||||
const primaryResult = await queryRunner.query(
|
||||
`
|
||||
SELECT id
|
||||
FROM data_sources
|
||||
WHERE type = 'static' AND scope = 'local' AND app_version_id = ANY($1::uuid[]) AND kind = $2
|
||||
LIMIT 1
|
||||
`,
|
||||
[appVersionIds, kind]
|
||||
);
|
||||
|
||||
if (primaryResult.length === 0) {
|
||||
console.log(`No data sources found for kind: ${kind}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const primaryDataSourceId = primaryResult[0].id;
|
||||
|
||||
// Ensure the primary data source has scope set to 'global', app_version_id set to null,
|
||||
// and organization_id set to the current organization
|
||||
await queryRunner.query(
|
||||
`
|
||||
UPDATE data_sources
|
||||
SET scope = 'global', app_version_id = NULL, organization_id = $1
|
||||
WHERE id = $2
|
||||
`,
|
||||
[orgId, primaryDataSourceId]
|
||||
);
|
||||
|
||||
console.log(
|
||||
`Updated primary data source ${primaryDataSourceId} scope to 'global', app_version_id to NULL, and organization_id to ${orgId}`
|
||||
);
|
||||
|
||||
// Get all other data source IDs of the same kind to be replaced
|
||||
const duplicatesResult = await queryRunner.query(
|
||||
`
|
||||
SELECT id
|
||||
FROM data_sources
|
||||
WHERE type = 'static' AND scope = 'local' AND
|
||||
app_version_id = ANY($1::uuid[]) AND
|
||||
kind = $2 AND
|
||||
id != $3
|
||||
`,
|
||||
[appVersionIds, kind, primaryDataSourceId]
|
||||
);
|
||||
|
||||
const duplicateDataSourceIds = duplicatesResult.map((row) => row.id);
|
||||
|
||||
if (duplicateDataSourceIds.length === 0) {
|
||||
console.log(`No duplicates found for kind: ${kind}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Found ${duplicateDataSourceIds.length} duplicate data sources for kind: ${kind}`);
|
||||
console.log(`Primary ID: ${primaryDataSourceId}, Duplicates: ${duplicateDataSourceIds.join(', ')}`);
|
||||
|
||||
// Update data_queries to use the primary data source
|
||||
const updateResult = await queryRunner.query(
|
||||
`
|
||||
UPDATE data_queries
|
||||
SET data_source_id = $1
|
||||
WHERE data_source_id = ANY($2::uuid[])
|
||||
`,
|
||||
[primaryDataSourceId, duplicateDataSourceIds]
|
||||
);
|
||||
|
||||
console.log(
|
||||
`Updated ${updateResult.length || 'multiple'} data_queries to use primary data source: ${primaryDataSourceId}`
|
||||
);
|
||||
|
||||
// Delete the duplicate data sources
|
||||
const deleteResult = await queryRunner.query(
|
||||
`
|
||||
DELETE FROM data_sources
|
||||
WHERE id = ANY($1::uuid[])
|
||||
`,
|
||||
[duplicateDataSourceIds]
|
||||
);
|
||||
|
||||
console.log(`Deleted ${deleteResult.length || duplicateDataSourceIds.length} duplicate data sources`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Data source consolidation complete.');
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// This is a data consolidation migration and cannot be easily reverted
|
||||
console.log(`
|
||||
WARNING: No down migration available for data consolidation.
|
||||
This migration consolidates duplicate data sources and cannot be automatically reverted.
|
||||
Please restore from a backup if needed.
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddMissingStaticDataSources1745409452884 implements MigrationInterface {
|
||||
private requiredDataSourceKinds = ['restapi', 'runjs', 'runpy', 'tooljetdb', 'workflows'];
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Get all organization IDs
|
||||
const organizationsResult = await queryRunner.query(`SELECT id FROM organizations`);
|
||||
const organizationIds = organizationsResult.map((row) => row.id);
|
||||
|
||||
console.log(`Found ${organizationIds.length} organizations to validate`);
|
||||
|
||||
// Process each organization
|
||||
for (const orgId of organizationIds) {
|
||||
console.log(`Processing organization: ${orgId}`);
|
||||
|
||||
// Get existing static data source kinds for this organization
|
||||
const existingDataSourcesResult = await queryRunner.query(
|
||||
`
|
||||
SELECT DISTINCT kind
|
||||
FROM data_sources
|
||||
WHERE type = 'static' AND scope = 'global' AND organization_id = $1
|
||||
`,
|
||||
[orgId]
|
||||
);
|
||||
|
||||
const existingKinds = existingDataSourcesResult.map((row) => row.kind);
|
||||
console.log(`Found existing data source kinds: ${existingKinds.join(', ') || 'none'}`);
|
||||
|
||||
// Find missing kinds
|
||||
const missingKinds = this.requiredDataSourceKinds.filter((kind) => !existingKinds.includes(kind));
|
||||
|
||||
if (missingKinds.length === 0) {
|
||||
console.log(`Organization ${orgId} has all required data source kinds`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Missing data source kinds for organization ${orgId}: ${missingKinds.join(', ')}`);
|
||||
|
||||
// Add missing data sources
|
||||
for (const kind of missingKinds) {
|
||||
const name = this.getDefaultNameForKind(kind);
|
||||
|
||||
await queryRunner.query(
|
||||
`
|
||||
INSERT INTO data_sources (
|
||||
name, kind, type, scope, organization_id, created_at, updated_at
|
||||
) VALUES (
|
||||
$1, $2, 'static', 'global', $3, NOW(), NOW()
|
||||
)
|
||||
`,
|
||||
[name, kind, orgId]
|
||||
);
|
||||
|
||||
console.log(`Added new data source: ${kind} for organization ${orgId}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Data source validation and addition complete.');
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// This is a data addition migration that adds missing required data sources
|
||||
console.log(`
|
||||
NOTE: Down migration is not implemented for data source validation.
|
||||
This migration adds missing required data sources to organizations.
|
||||
If needed, you could delete data sources by kind and organization manually.
|
||||
`);
|
||||
}
|
||||
|
||||
// Helper function to get default names for data source kinds
|
||||
private getDefaultNameForKind(kind: string): string {
|
||||
const nameMap: Record<string, string> = {
|
||||
restapi: 'restapidefault',
|
||||
runjs: 'runjsdefault',
|
||||
runpy: 'runpydefault',
|
||||
tooljetdb: 'tooljetdbdefault',
|
||||
workflows: 'workflowsdefault',
|
||||
};
|
||||
|
||||
return nameMap[kind] || `${kind}default`;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddDataSourceConstraintsForStatic1745409631920 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
console.log('Starting migration to add constraints to data_sources table');
|
||||
|
||||
// Add check constraint to ensure static type data sources have global scope
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE data_sources
|
||||
ADD CONSTRAINT chk_static_type_global_scope
|
||||
CHECK (type != 'static' OR scope = 'global');
|
||||
`);
|
||||
console.log('Added constraint: static type data sources must have global scope');
|
||||
|
||||
// Add unique constraint for combination of kind, type, and organization_id
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX idx_unique_static_kind_org
|
||||
ON public.data_sources (kind, type, organization_id)
|
||||
WHERE type = 'static';
|
||||
`);
|
||||
console.log('Added unique constraint on kind, type, and organization_id');
|
||||
|
||||
console.log('Migration completed successfully');
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
console.log('Starting rollback of data_sources constraints');
|
||||
|
||||
// Drop the unique constraint
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE public.data_sources
|
||||
DROP CONSTRAINT IF EXISTS idx_unique_static_kind_org;
|
||||
`);
|
||||
console.log('Dropped unique constraint on kind, type, and organization_id');
|
||||
|
||||
// Drop the check constraint
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE public.data_sources
|
||||
DROP CONSTRAINT IF EXISTS chk_static_type_global_scope;
|
||||
`);
|
||||
console.log('Dropped constraint: static type data sources must have global scope');
|
||||
|
||||
console.log('Rollback completed successfully');
|
||||
}
|
||||
}
|
||||
49
server/data-migrations/1752624000001-MigrateGroupSyncData.ts
Normal file
49
server/data-migrations/1752624000001-MigrateGroupSyncData.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { EntityManager, MigrationInterface, QueryRunner } from 'typeorm';
|
||||
import { processDataInBatches } from '@helpers/migration.helper';
|
||||
import { SSOConfigs } from '@entities/sso_config.entity';
|
||||
import { SsoConfigOidcGroupSync } from '@entities/sso_config_oidc_group_sync.entity';
|
||||
|
||||
export class MigrateGroupSyncData1752624000001 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const entityManager = queryRunner.manager;
|
||||
await processDataInBatches(
|
||||
entityManager,
|
||||
async (entityManager: EntityManager) => {
|
||||
return await entityManager
|
||||
.createQueryBuilder(SSOConfigs, 'sso_configs')
|
||||
.select(['sso_configs.id', 'sso_configs.configs'])
|
||||
.where('sso_configs.config_scope = :scope', { scope: 'organization' })
|
||||
.andWhere('sso_configs.sso = :sso', { sso: 'openid' })
|
||||
.getMany();
|
||||
},
|
||||
async (entityManager: EntityManager, ssoConfigs: SSOConfigs[]) => {
|
||||
await this.processUpdates(entityManager, ssoConfigs);
|
||||
},
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
private async processUpdates(entityManager: EntityManager, ssoConfigs: SSOConfigs[]) {
|
||||
for (const config of ssoConfigs) {
|
||||
const { id: ssoConfigId, configs } = config;
|
||||
if (!configs) {
|
||||
continue; // Skip if configs are not set or missing required fields
|
||||
}
|
||||
const { claimName, groupMapping, enableGroupSync } = configs as any;
|
||||
|
||||
const enrty = entityManager.create(SsoConfigOidcGroupSync, {
|
||||
ssoConfigId,
|
||||
organizationId: null,
|
||||
claimName: claimName || null,
|
||||
groupMapping: groupMapping || null,
|
||||
enableGroupSync: enableGroupSync || false,
|
||||
});
|
||||
await entityManager.save(SsoConfigOidcGroupSync, enrty);
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// Remove all rows from the sso_config_oidc_group_sync table
|
||||
await queryRunner.query(`DELETE FROM sso_config_oidc_group_sync`);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
|
||||
|
||||
export class CreateTablesForToojetAiConversations1740399879253 implements MigrationInterface {
|
||||
export class CreateTablesForToojetAiConversations1737530238311 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Create `ai_conversations` table
|
||||
await queryRunner.createTable(
|
||||
83
server/migrations/1742209024000-CreateTableGithubSSH.ts
Normal file
83
server/migrations/1742209024000-CreateTableGithubSSH.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
|
||||
|
||||
export class CreateTableGithubSSH1742209024000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'organization_git_ssh',
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'uuid',
|
||||
isGenerated: true,
|
||||
default: 'gen_random_uuid()',
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: 'config_id',
|
||||
type: 'uuid',
|
||||
isNullable: false,
|
||||
isUnique: true,
|
||||
},
|
||||
{
|
||||
name: 'git_url',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'ssh_private_key',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
isUnique: true,
|
||||
},
|
||||
{
|
||||
name: 'ssh_public_key',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
isUnique: true,
|
||||
},
|
||||
{
|
||||
name: 'key_type',
|
||||
type: 'enum',
|
||||
enumName: 'ssh_key_type',
|
||||
enum: ['rsa', 'ed25519'],
|
||||
default: "'ed25519'",
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'is_finalized',
|
||||
type: 'boolean',
|
||||
isNullable: false,
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
type: 'timestamp',
|
||||
isNullable: false,
|
||||
default: 'now()',
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
type: 'timestamp',
|
||||
isNullable: false,
|
||||
default: 'now()',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await queryRunner.createForeignKey(
|
||||
'organization_git_ssh',
|
||||
new TableForeignKey({
|
||||
columnNames: ['config_id'],
|
||||
referencedColumnNames: ['id'],
|
||||
referencedTableName: 'organization_git_sync',
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropTable('organization_git_ssh');
|
||||
}
|
||||
}
|
||||
95
server/migrations/1742215405123-CreateTableGithubHTTPS.ts
Normal file
95
server/migrations/1742215405123-CreateTableGithubHTTPS.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
|
||||
|
||||
export class CreateTableGithubHTTPS1742215405123 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'organization_git_https',
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'uuid',
|
||||
isGenerated: true,
|
||||
default: 'gen_random_uuid()',
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: 'config_id',
|
||||
type: 'uuid',
|
||||
isNullable: false,
|
||||
isUnique: true,
|
||||
},
|
||||
{
|
||||
name: 'https_url',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'github_branch',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'github_app_id',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'github_installation_id',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'github_enterprise_url',
|
||||
type: 'varchar',
|
||||
isNullable: true,
|
||||
default: null,
|
||||
},
|
||||
{
|
||||
name: 'github_enterprise_api_url',
|
||||
type: 'varchar',
|
||||
isNullable: true,
|
||||
default: null,
|
||||
},
|
||||
{
|
||||
name: 'github_private_key',
|
||||
type: 'text',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'is_finalized',
|
||||
type: 'boolean',
|
||||
isNullable: false,
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
type: 'timestamp',
|
||||
isNullable: false,
|
||||
default: 'now()',
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
type: 'timestamp',
|
||||
isNullable: false,
|
||||
default: 'now()',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await queryRunner.createForeignKey(
|
||||
'organization_git_https',
|
||||
new TableForeignKey({
|
||||
columnNames: ['config_id'],
|
||||
referencedColumnNames: ['id'],
|
||||
referencedTableName: 'organization_git_sync',
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropTable('organization_git_https');
|
||||
}
|
||||
}
|
||||
39
server/migrations/1742215921099-AddGitTypeEnumColumn.ts
Normal file
39
server/migrations/1742215921099-AddGitTypeEnumColumn.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddGitTypeEnumColumn1742215921099 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Create ENUM type
|
||||
await queryRunner.query(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_type WHERE typname = 'git_type'
|
||||
) THEN
|
||||
CREATE TYPE git_type AS ENUM (
|
||||
'github_ssh',
|
||||
'github_https',
|
||||
'disabled'
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
`);
|
||||
|
||||
// Add column with ENUM type
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE organization_git_sync
|
||||
ADD COLUMN IF NOT EXISTS git_type git_type NOT NULL DEFAULT 'github_ssh';
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// Revert migration: drop column and ENUM type
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE organization_git_sync
|
||||
DROP COLUMN IF EXISTS git_type;
|
||||
`);
|
||||
|
||||
await queryRunner.query(`
|
||||
DROP TYPE IF EXISTS git_type_enum;
|
||||
`);
|
||||
}
|
||||
}
|
||||
82
server/migrations/1746518671022-CreateTableGitLab.ts
Normal file
82
server/migrations/1746518671022-CreateTableGitLab.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
|
||||
|
||||
export class CreateTableGitLab1746518671022 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'organization_gitlab',
|
||||
columns: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'uuid',
|
||||
isGenerated: true,
|
||||
default: 'gen_random_uuid()',
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: 'config_id',
|
||||
type: 'uuid',
|
||||
isNullable: false,
|
||||
isUnique: true,
|
||||
},
|
||||
{
|
||||
name: 'gitlab_url',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'gitlab_branch',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'gitlab_project_id',
|
||||
type: 'varchar',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
name: 'gitlab_project_access_token',
|
||||
type: 'text',
|
||||
isNullable: true,
|
||||
default: null,
|
||||
},
|
||||
{
|
||||
name: 'gitlab_enterprise_url',
|
||||
type: 'varchar',
|
||||
isNullable: true,
|
||||
default: null,
|
||||
},
|
||||
{
|
||||
name: 'is_finalized',
|
||||
type: 'boolean',
|
||||
isNullable: false,
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
type: 'timestamp',
|
||||
isNullable: false,
|
||||
default: 'now()',
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
type: 'timestamp',
|
||||
isNullable: false,
|
||||
default: 'now()',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await queryRunner.createForeignKey(
|
||||
'organization_gitlab',
|
||||
new TableForeignKey({
|
||||
columnNames: ['config_id'],
|
||||
referencedColumnNames: ['id'],
|
||||
referencedTableName: 'organization_git_sync',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
11
server/migrations/1746526306001-AddGitLabEnum.ts
Normal file
11
server/migrations/1746526306001-AddGitLabEnum.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddGitLabEnum1746526306001 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TYPE "git_type" ADD VALUE 'gitlab';
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddWorkflowPermissionsInGroupPermissions1746705301652 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE permission_groups
|
||||
ADD COLUMN workflow_create BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN workflow_delete BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE permission_groups
|
||||
DROP COLUMN workflow_delete,
|
||||
DROP COLUMN workflow_create;
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddWorkflowTypeInResourceType1746705371665 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TYPE "resource_type" ADD VALUE IF NOT EXISTS 'workflow';
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddAppTypeInAppsGroupPermissions1746705448788 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'app_type') THEN
|
||||
CREATE TYPE "app_type" AS ENUM ('front-end', 'workflow');
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
ALTER TABLE "apps_group_permissions"
|
||||
ADD COLUMN "app_type" "app_type" NOT NULL DEFAULT 'front-end';
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "apps_group_permissions"
|
||||
DROP COLUMN "app_type";
|
||||
DROP TYPE IF EXISTS "app_type";
|
||||
`);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue