From cf5015b85d5cc82ec8b168c9cd50dd6610fa64f6 Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Tue, 25 Oct 2022 03:20:16 +0530 Subject: [PATCH 001/108] chore: previews for every PR release using Uffizzi add github workflows to take the prebuilt docker image for the PR to create a preview deployment on Uffizzi. Comment will be posted to every PR on a successful deployment. --- .github/uffizzi/docker-compose.uffizzi.yml | 24 ++++++ .github/workflows/release-pr.yml | 71 +++++++++++++++-- .github/workflows/uffizzi-preview.yml | 88 ++++++++++++++++++++++ 3 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 .github/uffizzi/docker-compose.uffizzi.yml create mode 100644 .github/workflows/uffizzi-preview.yml diff --git a/.github/uffizzi/docker-compose.uffizzi.yml b/.github/uffizzi/docker-compose.uffizzi.yml new file mode 100644 index 0000000000..eb33d7d3e9 --- /dev/null +++ b/.github/uffizzi/docker-compose.uffizzi.yml @@ -0,0 +1,24 @@ +version: '3' + +x-uffizzi: + ingress: + service: nocodb + port: 8080 + +services: + root_db: + image: postgres + restart: always + environment: + POSTGRES_PASSWORD: password + POSTGRES_USER: postgres + POSTGRES_DB: root_db + nocodb: + image: "${NOCODB_IMAGE}" + ports: + - "8080:8080" + restart: always + environment: + NC_DB: "pg://localhost:5432?u=postgres&p=password&d=root_db" + NC_ADMIN_EMAIL: admin@nocodb.com + NC_ADMIN_PASSWORD: password \ No newline at end of file diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index d104e79089..80c4780c31 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -6,7 +6,8 @@ on: # reopened: closed pull request is reopened # synchronize: commit(s) pushed to the pull request # ready_for_review: non PR release - types: [opened, reopened, synchronize, ready_for_review] + # closed: pull request is closed, used to delete uffizzi previews + types: [opened, reopened, synchronize, ready_for_review, closed] paths: - "packages/nocodb-sdk/**" - "packages/nc-gui/**" @@ -20,7 +21,7 @@ concurrency: jobs: # enrich tag for pr release set-tag: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} runs-on: 'ubuntu-latest' steps: - name: set-tag @@ -47,7 +48,7 @@ jobs: # Build, install, publish frontend and backend to npm release-npm: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} needs: [set-tag] uses: ./.github/workflows/release-npm.yml with: @@ -58,7 +59,7 @@ jobs: # Build docker image and push to docker hub release-docker: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} needs: [release-npm, set-tag] uses: ./.github/workflows/release-docker.yml with: @@ -72,7 +73,7 @@ jobs: # Build executables and publish to GitHub release-executables: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} needs: [set-tag, release-npm] uses: ./.github/workflows/release-timely-executables.yml with: @@ -82,7 +83,7 @@ jobs: # Add a comment for PR docker build leave-comment: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} runs-on: 'ubuntu-latest' needs: [release-docker, set-tag] steps: @@ -94,9 +95,45 @@ jobs: docker run -d -p 8888:8080 nocodb/nocodb-timely:${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }} ``` + # Create a preview for the pull request + preview-pull-request: + name: "Trigger Uffizzi Preview" + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} + runs-on: 'ubuntu-latest' + needs: [release-docker, set-tag] + outputs: + compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }} + steps: + - name: Checkout git repo + uses: actions/checkout@v3 + - name: Render Compose File + run: | + NOCODB_IMAGE=nocodb/nocodb-timely:${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }} + export NOCODB_IMAGE + # Render simple template from environment variables. + envsubst < .github/uffizzi/docker-compose.uffizzi.yml > docker-compose.rendered.yml + cat docker-compose.rendered.yml + - name: Upload Rendered Compose File as Artifact + uses: actions/upload-artifact@v3 + with: + name: preview-spec + path: docker-compose.rendered.yml + retention-days: 2 + - name: Serialize PR Event to File + run: | + cat << EOF > event.json + ${{ toJSON(github.event) }} + EOF + - name: Upload PR Event as Artifact + uses: actions/upload-artifact@v3 + with: + name: preview-spec + path: event.json + retention-days: 2 + # Add a comment for PR executable build leave-executable-comment: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action != 'closed' }} runs-on: 'ubuntu-latest' needs: [release-executables, set-tag] steps: @@ -129,3 +166,23 @@ jobs: ``` For executables visit [here](https://github.com/nocodb/nocodb-timely/releases/tag/${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }}) + + # delete the uffizzi preview created off of this PR + delete-uffizzi-preview: + name: Call for Preview Deletion + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false && github.base_ref == 'develop' && github.event.action == 'closed' }} + steps: + # If this PR is closing, we will not render a compose file nor pass it to the next workflow. + - name: Serialize PR Event to File + run: | + cat << EOF > event.json + ${{ toJSON(github.event) }} + + EOF + - name: Upload PR Event as Artifact + uses: actions/upload-artifact@v3 + with: + name: preview-spec + path: event.json + retention-days: 2 \ No newline at end of file diff --git a/.github/workflows/uffizzi-preview.yml b/.github/workflows/uffizzi-preview.yml new file mode 100644 index 0000000000..470acaa0e0 --- /dev/null +++ b/.github/workflows/uffizzi-preview.yml @@ -0,0 +1,88 @@ +name: Deploy Uffizzi Preview + +on: + workflow_run: + workflows: + - "PR Release" + types: + - completed + +jobs: + cache-compose-file: + name: Cache Compose File + runs-on: ubuntu-latest + outputs: + compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }} + pr-number: ${{ env.PR_NUMBER }} + steps: + - name: 'Download artifacts' + # Fetch output (zip archive) from the workflow run that triggered this workflow. + uses: actions/github-script@v6 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "preview-spec" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data)); + + - name: 'Unzip artifact' + run: unzip preview-spec.zip + + - name: Read Event into ENV + run: | + echo 'EVENT_JSON<> $GITHUB_ENV + cat event.json >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Hash Rendered Compose File + id: hash + # If the previous workflow was triggered by a PR close event, we will not have a compose file artifact. + if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }} + run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV + + - name: Cache Rendered Compose File + if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }} + uses: actions/cache@v3 + with: + path: docker-compose.rendered.yml + key: ${{ env.COMPOSE_FILE_HASH }} + + - name: Read PR Number From Event Object + id: pr + run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV + + - name: DEBUG - Print Job Outputs + if: ${{ runner.debug }} + run: | + echo "PR number: ${{ env.PR_NUMBER }}" + echo "Compose file hash: ${{ env.COMPOSE_FILE_HASH }}" + cat event.json + + deploy-uffizzi-preview: + name: Use Remote Workflow to Preview on Uffizzi + needs: + - cache-compose-file + uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2 + with: + # If this workflow was triggered by a PR close event, cache-key will be an empty string + # and this reusable workflow will delete the preview deployment. + compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }} + compose-file-cache-path: docker-compose.rendered.yml + server: https://app.uffizzi.com + pr-number: ${{ needs.cache-compose-file.outputs.pr-number }} + permissions: + contents: read + pull-requests: write + id-token: write \ No newline at end of file From 0662e1d32f6d5c042f11dbab87908332aa7a749f Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Thu, 1 Dec 2022 14:13:20 +0530 Subject: [PATCH 002/108] feat(playwright): Added tests for single select, multi select, single line text, long text and attachments --- .../pages/Dashboard/common/Cell/index.ts | 17 +- tests/playwright/tests/gridOperations.spec.ts | 117 ----------- .../tests/keyboardShortcuts.spec.ts | 192 ++++++++++++++++++ 3 files changed, 208 insertions(+), 118 deletions(-) delete mode 100644 tests/playwright/tests/gridOperations.spec.ts diff --git a/tests/playwright/pages/Dashboard/common/Cell/index.ts b/tests/playwright/pages/Dashboard/common/Cell/index.ts index 22052a2ca6..4e1ddb538e 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/index.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/index.ts @@ -51,7 +51,20 @@ export class CellPageObject extends BasePage { index, columnHeader, }); - await this.get({ index, columnHeader }).locator('input').fill(text); + const isInputBox = async () => (await this.get({ index, columnHeader }).locator('input').count()) > 0; + + for (let i = 0; i < 10; i++) { + if (await isInputBox()) { + break; + } + await this.rootPage.waitForTimeout(200); + } + + if (await isInputBox()) { + await this.get({ index, columnHeader }).locator('input').fill(text); + } else { + await this.get({ index, columnHeader }).locator('textarea').fill(text); + } } async inCellExpand({ index, columnHeader }: { index: number; columnHeader: string }) { @@ -192,7 +205,9 @@ export class CellPageObject extends BasePage { ...clickOptions: Parameters ) { await this.get({ index, columnHeader }).click(...clickOptions); + await (await this.get({ index, columnHeader }).elementHandle()).waitForElementState('stable'); await this.get({ index, columnHeader }).press((await this.isMacOs()) ? 'Meta+C' : 'Control+C'); + await this.verifyToast({ message: 'Copied to clipboard' }); } } diff --git a/tests/playwright/tests/gridOperations.spec.ts b/tests/playwright/tests/gridOperations.spec.ts deleted file mode 100644 index d05b9fcc54..0000000000 --- a/tests/playwright/tests/gridOperations.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { DashboardPage } from '../pages/Dashboard'; -import setup from '../setup'; - -test.describe('Grid operations', () => { - let dashboard: DashboardPage; - let context: any; - - test.beforeEach(async ({ page }) => { - context = await setup({ page }); - dashboard = new DashboardPage(page, context.project); - }); - - test('Clipboard support', async () => { - // close 'Team & Auth' tab - await dashboard.closeTab({ title: 'Team & Auth' }); - - await dashboard.treeView.createTable({ title: 'Sheet1' }); - - await dashboard.grid.column.create({ - title: 'Number', - type: 'Number', - }); - await dashboard.grid.column.create({ - title: 'Checkbox', - type: 'Checkbox', - }); - await dashboard.grid.column.create({ - title: 'Date', - type: 'Date', - }); - await dashboard.grid.column.create({ - title: 'Attachment', - type: 'Attachment', - }); - - await dashboard.grid.addNewRow({ - index: 0, - }); - await dashboard.grid.cell.click({ - index: 0, - columnHeader: 'Number', - }); - await dashboard.grid.cell.fillText({ - index: 0, - columnHeader: 'Number', - text: '123', - }); - await dashboard.grid.cell.click({ - index: 0, - columnHeader: 'Checkbox', - }); - - const today = new Date().toISOString().slice(0, 10); - await dashboard.grid.cell.date.open({ - index: 0, - columnHeader: 'Date', - }); - await dashboard.grid.cell.date.selectDate({ - date: today, - }); - await dashboard.grid.cell.date.close(); - - await dashboard.grid.cell.attachment.addFile({ - index: 0, - columnHeader: 'Attachment', - filePath: `${process.cwd()}/fixtures/sampleFiles/1.json`, - }); - - await dashboard.grid.cell.copyToClipboard({ - index: 0, - columnHeader: 'Title', - }); - expect(await dashboard.grid.cell.getClipboardText()).toBe('Row 0'); - - await dashboard.grid.cell.copyToClipboard({ - index: 0, - columnHeader: 'Number', - }); - expect(await dashboard.grid.cell.getClipboardText()).toBe('123'); - - await dashboard.grid.cell.copyToClipboard( - { - index: 0, - columnHeader: 'Checkbox', - }, - { position: { x: 1, y: 1 } } - ); - await new Promise(resolve => setTimeout(resolve, 5000)); - expect(await dashboard.grid.cell.getClipboardText()).toBe('true'); - - await dashboard.grid.cell.click({ - index: 0, - columnHeader: 'Checkbox', - }); - await dashboard.grid.cell.copyToClipboard( - { - index: 0, - columnHeader: 'Checkbox', - }, - { position: { x: 1, y: 1 } } - ); - expect(await dashboard.grid.cell.getClipboardText()).toBe('false'); - - await dashboard.grid.cell.copyToClipboard({ - index: 0, - columnHeader: 'Date', - }); - expect(await dashboard.grid.cell.getClipboardText()).toBe(today); - - await dashboard.grid.cell.copyToClipboard({ - index: 0, - columnHeader: 'Attachment', - }); - // expect(await dashboard.grid.cell.getClipboardText()).toBe('1.json'); - }); -}); diff --git a/tests/playwright/tests/keyboardShortcuts.spec.ts b/tests/playwright/tests/keyboardShortcuts.spec.ts index 168bbe948c..fe21e61855 100644 --- a/tests/playwright/tests/keyboardShortcuts.spec.ts +++ b/tests/playwright/tests/keyboardShortcuts.spec.ts @@ -101,4 +101,196 @@ test.describe('Verify shortcuts', () => { await page.waitForTimeout(2000); await grid.cell.verify({ index: 1, columnHeader: 'Country', value: 'NewAlgeria' }); }); + + test('Clipboard support for cells', async () => { + // close 'Team & Auth' tab + await dashboard.closeTab({ title: 'Team & Auth' }); + + await dashboard.treeView.createTable({ title: 'Sheet1' }); + + await dashboard.grid.column.create({ + title: 'SingleLineText', + type: 'SingleLineText', + }); + await dashboard.grid.column.create({ + title: 'LongText', + type: 'LongText', + }); + await dashboard.grid.column.create({ + title: 'Number', + type: 'Number', + }); + await dashboard.grid.column.create({ + title: 'SingleSelect', + type: 'SingleSelect', + }); + await dashboard.grid.column.create({ + title: 'MultiSelect', + type: 'MultiSelect', + }); + await dashboard.grid.column.create({ + title: 'Checkbox', + type: 'Checkbox', + }); + await dashboard.grid.column.create({ + title: 'Date', + type: 'Date', + }); + await dashboard.grid.column.create({ + title: 'Attachment', + type: 'Attachment', + }); + + // ######################################## + + await dashboard.grid.addNewRow({ + index: 0, + }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'SingleLineText', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'SingleLineText', + text: 'SingleLineText', + }); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'LongText', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'LongText', + text: 'LongText', + }); + + await grid.cell.selectOption.select({ index: 0, columnHeader: 'SingleSelect', option: 'Option 1' }); + await grid.cell.selectOption.select({ + index: 0, + columnHeader: 'MultiSelect', + option: 'Option 2', + multiSelect: true, + }); + await grid.cell.selectOption.select({ + index: 0, + columnHeader: 'MultiSelect', + option: 'Option 1', + multiSelect: true, + }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Number', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Number', + text: '123', + }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Checkbox', + }); + + const today = new Date().toISOString().slice(0, 10); + await dashboard.grid.cell.date.open({ + index: 0, + columnHeader: 'Date', + }); + await dashboard.grid.cell.date.selectDate({ + date: today, + }); + await dashboard.grid.cell.date.close(); + + await dashboard.grid.cell.attachment.addFile({ + index: 0, + columnHeader: 'Attachment', + filePath: `${process.cwd()}/fixtures/sampleFiles/1.json`, + }); + + // ######################################## + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'SingleLineText', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('SingleLineText'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'LongText', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('LongText'); + + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'SingleSelect', + }, + { position: { x: 1, y: 1 } } + ); + expect(await dashboard.grid.cell.getClipboardText()).toBe('Option 1'); + + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'MultiSelect', + }, + { position: { x: 1, y: 1 } } + ); + expect(await dashboard.grid.cell.getClipboardText()).toContain('Option 1'); + expect(await dashboard.grid.cell.getClipboardText()).toContain('Option 2'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Title', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('Row 0'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Number', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('123'); + + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'Checkbox', + }, + { position: { x: 1, y: 1 } } + ); + await new Promise(resolve => setTimeout(resolve, 5000)); + expect(await dashboard.grid.cell.getClipboardText()).toBe('true'); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Checkbox', + }); + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'Checkbox', + }, + { position: { x: 1, y: 1 } } + ); + expect(await dashboard.grid.cell.getClipboardText()).toBe('false'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Date', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe(today); + + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'Attachment', + }, + { position: { x: 1, y: 1 } } + ); + const attachmentsInfo = JSON.parse(await dashboard.grid.cell.getClipboardText()); + expect(attachmentsInfo[0]['title']).toBe('1.json'); + }); }); From 66b6d650415f1ac04f9c87850cb0a1c39af2d517 Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Thu, 1 Dec 2022 14:24:49 +0530 Subject: [PATCH 003/108] feat(playwright): Added tests for Phone number --- .../tests/keyboardShortcuts.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/playwright/tests/keyboardShortcuts.spec.ts b/tests/playwright/tests/keyboardShortcuts.spec.ts index fe21e61855..54a1ee136b 100644 --- a/tests/playwright/tests/keyboardShortcuts.spec.ts +++ b/tests/playwright/tests/keyboardShortcuts.spec.ts @@ -120,6 +120,10 @@ test.describe('Verify shortcuts', () => { title: 'Number', type: 'Number', }); + await dashboard.grid.column.create({ + title: 'PhoneNumber', + type: 'PhoneNumber', + }); await dashboard.grid.column.create({ title: 'SingleSelect', type: 'SingleSelect', @@ -188,6 +192,15 @@ test.describe('Verify shortcuts', () => { columnHeader: 'Number', text: '123', }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'PhoneNumber', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'PhoneNumber', + text: '987654321', + }); await dashboard.grid.cell.click({ index: 0, columnHeader: 'Checkbox', @@ -254,6 +267,12 @@ test.describe('Verify shortcuts', () => { }); expect(await dashboard.grid.cell.getClipboardText()).toBe('123'); + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'PhoneNumber', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('987654321'); + await dashboard.grid.cell.copyToClipboard( { index: 0, From 868b109ae5f7be869266e9f368ecd1c01f4dcdb5 Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Thu, 1 Dec 2022 14:38:37 +0530 Subject: [PATCH 004/108] feat(playwright): Added tests for Email, URL, Decimal and Percent --- .../tests/keyboardShortcuts.spec.ts | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/tests/playwright/tests/keyboardShortcuts.spec.ts b/tests/playwright/tests/keyboardShortcuts.spec.ts index 54a1ee136b..d9af966294 100644 --- a/tests/playwright/tests/keyboardShortcuts.spec.ts +++ b/tests/playwright/tests/keyboardShortcuts.spec.ts @@ -124,6 +124,22 @@ test.describe('Verify shortcuts', () => { title: 'PhoneNumber', type: 'PhoneNumber', }); + await dashboard.grid.column.create({ + title: 'Email', + type: 'Email', + }); + await dashboard.grid.column.create({ + title: 'URL', + type: 'URL', + }); + await dashboard.grid.column.create({ + title: 'Decimal', + type: 'Decimal', + }); + await dashboard.grid.column.create({ + title: 'Percent', + type: 'Percent', + }); await dashboard.grid.column.create({ title: 'SingleSelect', type: 'SingleSelect', @@ -201,6 +217,47 @@ test.describe('Verify shortcuts', () => { columnHeader: 'PhoneNumber', text: '987654321', }); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Email', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Email', + text: 'test@example.com', + }); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'URL', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'URL', + text: 'nocodb.com', + }); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Decimal', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Decimal', + text: '1.1', + }); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Percent', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Percent', + text: '80', + }); + await dashboard.grid.cell.click({ index: 0, columnHeader: 'Checkbox', @@ -222,7 +279,7 @@ test.describe('Verify shortcuts', () => { filePath: `${process.cwd()}/fixtures/sampleFiles/1.json`, }); - // ######################################## + // // ######################################## await dashboard.grid.cell.copyToClipboard({ index: 0, @@ -273,6 +330,30 @@ test.describe('Verify shortcuts', () => { }); expect(await dashboard.grid.cell.getClipboardText()).toBe('987654321'); + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Email', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('test@example.com'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'URL', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('nocodb.com'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Decimal', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('1.1'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Percent', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('80'); + await dashboard.grid.cell.copyToClipboard( { index: 0, From 295d0deaa8bf85051125ea8d7763a40b02d7fe88 Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Thu, 1 Dec 2022 18:34:43 +0530 Subject: [PATCH 005/108] feat(playwright): Added tests for Currency, Duration, Rating --- .../pages/Dashboard/Grid/Column/index.ts | 16 ++--- .../pages/Dashboard/common/Cell/RatingCell.ts | 8 +++ .../tests/keyboardShortcuts.spec.ts | 60 ++++++++++++++++++- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/tests/playwright/pages/Dashboard/Grid/Column/index.ts b/tests/playwright/pages/Dashboard/Grid/Column/index.ts index 48107c7800..370ee2894d 100644 --- a/tests/playwright/pages/Dashboard/Grid/Column/index.ts +++ b/tests/playwright/pages/Dashboard/Grid/Column/index.ts @@ -34,7 +34,7 @@ export class ColumnPageObject extends BasePage { childColumn = '', relationType = '', rollupType = '', - format = '', + format, insertAfterColumnTitle, insertBeforeColumnTitle, }: { @@ -81,12 +81,14 @@ export class ColumnPageObject extends BasePage { }); break; case 'Duration': - await this.get().locator('.ant-select-single').nth(1).click(); - await this.rootPage - .locator(`.ant-select-item`, { - hasText: format, - }) - .click(); + if (format) { + await this.get().locator('.ant-select-single').nth(1).click(); + await this.rootPage + .locator(`.ant-select-item`, { + hasText: format, + }) + .click(); + } break; case 'Formula': await this.get().locator('.nc-formula-input').fill(formula); diff --git a/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts b/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts index e5ec9a7bc9..da8b44d9f7 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts @@ -14,6 +14,14 @@ export class RatingCellPageObject extends BasePage { return this.cell.get({ index, columnHeader }); } + async select({ index, columnHeader, rating }: { index?: number; columnHeader: string; rating: number }) { + await this.waitForResponse({ + uiAction: this.get({ index, columnHeader }).locator('.ant-rate-star > div').nth(rating).click(), + httpMethodsToMatch: ['POST', 'PATCH'], + requestUrlPathToMatch: 'api/v1/db/data/noco/', + }); + } + async verify({ index, columnHeader, rating }: { index?: number; columnHeader: string; rating: number }) { await expect(await this.get({ index, columnHeader }).locator(`div[role="radio"][aria-checked="true"]`)).toHaveCount( rating diff --git a/tests/playwright/tests/keyboardShortcuts.spec.ts b/tests/playwright/tests/keyboardShortcuts.spec.ts index d9af966294..d9de119030 100644 --- a/tests/playwright/tests/keyboardShortcuts.spec.ts +++ b/tests/playwright/tests/keyboardShortcuts.spec.ts @@ -140,6 +140,18 @@ test.describe('Verify shortcuts', () => { title: 'Percent', type: 'Percent', }); + await dashboard.grid.column.create({ + title: 'Currency', + type: 'Currency', + }); + await dashboard.grid.column.create({ + title: 'Duration', + type: 'Duration', + }); + await dashboard.grid.column.create({ + title: 'Rating', + type: 'Rating', + }); await dashboard.grid.column.create({ title: 'SingleSelect', type: 'SingleSelect', @@ -258,6 +270,31 @@ test.describe('Verify shortcuts', () => { text: '80', }); + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Currency', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Currency', + text: '20', + }); + + await dashboard.grid.cell.click({ + index: 0, + columnHeader: 'Duration', + }); + await dashboard.grid.cell.fillText({ + index: 0, + columnHeader: 'Duration', + text: '0008', + }); + + await dashboard.grid.cell.rating.select({ + index: 0, + columnHeader: 'Rating', + rating: 3, + }); await dashboard.grid.cell.click({ index: 0, columnHeader: 'Checkbox', @@ -279,7 +316,7 @@ test.describe('Verify shortcuts', () => { filePath: `${process.cwd()}/fixtures/sampleFiles/1.json`, }); - // // ######################################## + // ######################################## await dashboard.grid.cell.copyToClipboard({ index: 0, @@ -354,6 +391,27 @@ test.describe('Verify shortcuts', () => { }); expect(await dashboard.grid.cell.getClipboardText()).toBe('80'); + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Currency', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('20'); + + await dashboard.grid.cell.copyToClipboard({ + index: 0, + columnHeader: 'Duration', + }); + expect(await dashboard.grid.cell.getClipboardText()).toBe('480'); + + await dashboard.grid.cell.copyToClipboard( + { + index: 0, + columnHeader: 'Rating', + }, + { position: { x: 1, y: 1 } } + ); + expect(await dashboard.grid.cell.getClipboardText()).toBe('4'); + await dashboard.grid.cell.copyToClipboard( { index: 0, From b1b362366ee7f6f549d84d7a64e55fb6eb85697c Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Fri, 2 Dec 2022 14:55:02 +0800 Subject: [PATCH 006/108] fix(nc-gui): type issues --- packages/nc-gui/components/cell/Json.vue | 4 +- packages/nc-gui/components/cell/TextArea.vue | 2 +- .../components/cell/attachment/Carousel.vue | 4 +- .../nc-gui/components/general/Tooltip.vue | 11 +++++- .../components/smartsheet/ApiSnippet.vue | 9 +---- .../nc-gui/components/smartsheet/Grid.vue | 13 ++++--- .../components/smartsheet/VirtualCell.vue | 2 +- .../smartsheet/column/EditOrAddProvider.vue | 2 +- .../column/LinkedToAnotherRecordOptions.vue | 10 +++-- .../smartsheet/column/LookupOptions.vue | 39 ++++++++----------- .../smartsheet/column/RollupOptions.vue | 32 +++++++-------- .../components/smartsheet/column/utils.ts | 9 +++++ .../components/smartsheet/header/Cell.vue | 7 +++- .../smartsheet/header/VirtualCell.vue | 2 +- .../toolbar/KanbanStackEditOrAdd.vue | 12 +++++- .../smartsheet/toolbar/MoreActions.vue | 4 +- .../smartsheet/toolbar/SearchData.vue | 7 ++-- .../smartsheet/toolbar/SortListMenu.vue | 9 ++++- .../smartsheet/toolbar/ViewActions.vue | 24 ++++++------ .../components/tabs/auth/UserManagement.vue | 2 +- .../tabs/auth/user-management/UsersModal.vue | 2 +- .../nc-gui/components/template/Editor.vue | 11 +----- .../components/ListChildItems.vue | 26 +++++++------ packages/nc-gui/components/webhook/List.vue | 2 +- .../composables/useColumnCreateStore.ts | 11 ++++-- .../useExpandedFormDetached/index.ts | 2 +- packages/nc-gui/composables/useLTARStore.ts | 2 +- packages/nc-gui/composables/useSharedView.ts | 5 +-- .../nc-gui/composables/useSmartsheetStore.ts | 2 +- packages/nc-gui/package-lock.json | 11 ++++++ packages/nc-gui/package.json | 1 + .../form/[viewId]/index/index.vue | 5 ++- packages/nc-gui/pages/forgot-password.vue | 5 ++- .../nc-gui/pages/index/index/[projectId].vue | 3 +- packages/nc-gui/pages/index/index/create.vue | 3 +- packages/nc-gui/pages/projects/index.vue | 3 +- .../nc-gui/pages/projects/index/index.vue | 5 +-- packages/nc-gui/pages/signup/[[token]].vue | 9 +++-- packages/nc-gui/utils/sortUtils.ts | 2 +- packages/nc-gui/utils/viewUtils.ts | 8 +++- packages/nc-gui/web-types.json | 4 ++ 41 files changed, 190 insertions(+), 136 deletions(-) create mode 100644 packages/nc-gui/components/smartsheet/column/utils.ts diff --git a/packages/nc-gui/components/cell/Json.vue b/packages/nc-gui/components/cell/Json.vue index f89ddf87aa..8d8d6b9831 100644 --- a/packages/nc-gui/components/cell/Json.vue +++ b/packages/nc-gui/components/cell/Json.vue @@ -135,13 +135,13 @@ useSelectedCellKeyupListener(active, (e) => {
Cancel
-
Save
+
Save
() const emits = defineEmits(['update:modelValue']) diff --git a/packages/nc-gui/components/cell/attachment/Carousel.vue b/packages/nc-gui/components/cell/attachment/Carousel.vue index 4933e7d396..63a7c1bb0f 100644 --- a/packages/nc-gui/components/cell/attachment/Carousel.vue +++ b/packages/nc-gui/components/cell/attachment/Carousel.vue @@ -47,7 +47,7 @@ onClickOutside(carouselRef, () => { diff --git a/packages/nc-gui/components/dashboard/settings/DataSources.vue b/packages/nc-gui/components/dashboard/settings/DataSources.vue new file mode 100644 index 0000000000..8f4d227157 --- /dev/null +++ b/packages/nc-gui/components/dashboard/settings/DataSources.vue @@ -0,0 +1,101 @@ + + + diff --git a/packages/nc-gui/components/dashboard/settings/Modal.vue b/packages/nc-gui/components/dashboard/settings/Modal.vue index db7a2bd156..c0c9f0310c 100644 --- a/packages/nc-gui/components/dashboard/settings/Modal.vue +++ b/packages/nc-gui/components/dashboard/settings/Modal.vue @@ -1,5 +1,6 @@ + + + + diff --git a/packages/nc-gui/components/dlg/TableCreate.vue b/packages/nc-gui/components/dlg/TableCreate.vue index e950102b36..96cb6cec16 100644 --- a/packages/nc-gui/components/dlg/TableCreate.vue +++ b/packages/nc-gui/components/dlg/TableCreate.vue @@ -4,6 +4,7 @@ import { TabType } from '~/lib' const props = defineProps<{ modelValue: boolean + baseId: string }>() const emit = defineEmits(['update:modelValue']) @@ -28,7 +29,7 @@ const { table, createTable, generateUniqueTitle, tables, project } = useTable(as }) dialogShow.value = false -}) +}, props.baseId) const useForm = Form.useForm @@ -51,11 +52,11 @@ const validators = computed(() => { validator: (rule: any, value: any) => { return new Promise((resolve, reject) => { let tableNameLengthLimit = 255 - if (isMysql) { + if (isMysql(props.baseId)) { tableNameLengthLimit = 64 - } else if (isPg) { + } else if (isPg(props.baseId)) { tableNameLengthLimit = 63 - } else if (isMssql) { + } else if (isMssql(props.baseId)) { tableNameLengthLimit = 128 } const projectPrefix = project?.value?.prefix || '' diff --git a/packages/nc-gui/components/template/Editor.vue b/packages/nc-gui/components/template/Editor.vue index 2f99813c49..24fb40b834 100644 --- a/packages/nc-gui/components/template/Editor.vue +++ b/packages/nc-gui/components/template/Editor.vue @@ -66,7 +66,7 @@ const { addTab } = useTabs() const { sqlUis, project, loadTables } = useProject() -const sqlUi = ref(meta.value?.base_id ? sqlUis.value[meta.value?.base_id] : sqlUis.value[0]) +const sqlUi = ref(meta.value?.base_id ? sqlUis.value[meta.value?.base_id] : Object.values(sqlUis.value)[0]) const hasSelectColumn = ref([]) diff --git a/packages/nc-gui/composables/useColumnCreateStore.ts b/packages/nc-gui/composables/useColumnCreateStore.ts index a8d8d0c68a..d31ac88e16 100644 --- a/packages/nc-gui/composables/useColumnCreateStore.ts +++ b/packages/nc-gui/composables/useColumnCreateStore.ts @@ -36,7 +36,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState const { $e } = useNuxtApp() - const sqlUi = ref(meta.value?.base_id ? sqlUis.value[meta.value?.base_id] : sqlUis.value[0]) + const sqlUi = ref(meta.value?.base_id ? sqlUis.value[meta.value?.base_id] : Object.values(sqlUis.value)[0]) const isEdit = computed(() => !!column?.value?.id) diff --git a/packages/nc-gui/composables/useSmartsheetStore.ts b/packages/nc-gui/composables/useSmartsheetStore.ts index 934924154d..a0982e4b66 100644 --- a/packages/nc-gui/composables/useSmartsheetStore.ts +++ b/packages/nc-gui/composables/useSmartsheetStore.ts @@ -15,7 +15,7 @@ const [useProvideSmartsheetStore, useSmartsheetStore] = useInjectionState( const { $api } = useNuxtApp() const { sqlUis } = useProject() - const sqlUi = ref(meta.value?.base_id ? sqlUis.value[meta.value?.base_id] : sqlUis.value[0]) + const sqlUi = ref(meta.value?.base_id ? sqlUis.value[meta.value?.base_id] : Object.values(sqlUis.value)[0]) const cellRefs = ref([]) diff --git a/packages/nc-gui/composables/useTable.ts b/packages/nc-gui/composables/useTable.ts index b95a3c880b..3de4ddde10 100644 --- a/packages/nc-gui/composables/useTable.ts +++ b/packages/nc-gui/composables/useTable.ts @@ -16,7 +16,7 @@ import { } from '#imports' import { TabType } from '~/lib' -export function useTable(onTableCreate?: (tableMeta: TableType) => void) { +export function useTable(onTableCreate?: (tableMeta: TableType) => void, baseId?: string) { const table = reactive<{ title: string; table_name: string; columns: string[] }>({ title: '', table_name: '', @@ -34,7 +34,7 @@ export function useTable(onTableCreate?: (tableMeta: TableType) => void) { const { closeTab } = useTabs() const { sqlUis, project, tables } = useProject() - const sqlUi = ref(sqlUis.value[0]) + const sqlUi = computed(() => (baseId && sqlUis.value[baseId] ? sqlUis.value[baseId] : Object.values(sqlUis.value)[0])) const createTable = async () => { if (!sqlUi?.value) return @@ -49,7 +49,7 @@ export function useTable(onTableCreate?: (tableMeta: TableType) => void) { }) try { - const tableMeta = await $api.dbTable.create(project?.value?.id as string, { + const tableMeta = await $api.base.tableCreate(project?.value?.id as string, baseId as string, { ...table, columns, }) From 743c160eef57b081207bf3fa34f86395e52a600e Mon Sep 17 00:00:00 2001 From: mertmit Date: Sat, 24 Sep 2022 17:34:21 +0300 Subject: [PATCH 021/108] fix: multiple source project prefix Signed-off-by: mertmit --- packages/nc-gui/composables/useTable.ts | 2 +- packages/nocodb/src/lib/meta/api/tableApis.ts | 7 ++++--- packages/nocodb/src/lib/meta/helpers/getTableName.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/nc-gui/composables/useTable.ts b/packages/nc-gui/composables/useTable.ts index 3de4ddde10..6b9368c0c3 100644 --- a/packages/nc-gui/composables/useTable.ts +++ b/packages/nc-gui/composables/useTable.ts @@ -63,7 +63,7 @@ export function useTable(onTableCreate?: (tableMeta: TableType) => void, baseId? watch( () => table.title, (title) => { - table.table_name = `${project?.value?.prefix || ''}${title}` + table.table_name = `${title}` }, ) diff --git a/packages/nocodb/src/lib/meta/api/tableApis.ts b/packages/nocodb/src/lib/meta/api/tableApis.ts index 778a553b3b..89df8a5072 100644 --- a/packages/nocodb/src/lib/meta/api/tableApis.ts +++ b/packages/nocodb/src/lib/meta/api/tableApis.ts @@ -90,6 +90,7 @@ export async function tableList(req: Request, res: Response) { export async function tableCreate(req: Request, res) { const project = await Project.getWithInfo(req.params.projectId); let base = project.bases[0]; + if (req.params.baseId) { base = project.bases.find((b) => b.id === req.params.baseId); } @@ -103,7 +104,7 @@ export async function tableCreate(req: Request, res) { ); } - if (project.prefix) { + if (base.is_meta && project.prefix) { if (!req.body.table_name.startsWith(project.prefix)) { req.body.table_name = `${project.prefix}_${req.body.table_name}`; } @@ -228,14 +229,14 @@ export async function tableUpdate(req: Request, res) { const project = await Project.getWithInfo(req.body.project_id); const base = project.bases.find((b) => b.id === model.base_id); - + if (!req.body.table_name) { NcError.badRequest( 'Missing table name `table_name` property in request body' ); } - if (project.prefix) { + if (base.is_meta && project.prefix) { if (!req.body.table_name.startsWith(project.prefix)) { req.body.table_name = `${project.prefix}${req.body.table_name}`; } diff --git a/packages/nocodb/src/lib/meta/helpers/getTableName.ts b/packages/nocodb/src/lib/meta/helpers/getTableName.ts index cff2d54cbd..4dc49ba652 100644 --- a/packages/nocodb/src/lib/meta/helpers/getTableName.ts +++ b/packages/nocodb/src/lib/meta/helpers/getTableName.ts @@ -7,7 +7,7 @@ export default function getTableNameAlias( base: Base ): string { let tn = tableName; - if (prefix) { + if (base.is_meta && prefix) { tn = tn.replace(prefix, ''); } From 1cf10e8fd91bf3c01c0be32f0c775a9259dc5cf1 Mon Sep 17 00:00:00 2001 From: mertmit Date: Sat, 24 Sep 2022 17:58:15 +0300 Subject: [PATCH 022/108] fix: multiple source sync views Signed-off-by: mertmit --- packages/nocodb/src/lib/meta/api/baseApis.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nocodb/src/lib/meta/api/baseApis.ts b/packages/nocodb/src/lib/meta/api/baseApis.ts index 7b015135b7..b21985a000 100644 --- a/packages/nocodb/src/lib/meta/api/baseApis.ts +++ b/packages/nocodb/src/lib/meta/api/baseApis.ts @@ -126,14 +126,14 @@ async function populateMeta(base: Base, project: Project): Promise { return t; }); - // TODO add base prefix - /* filter based on prefix - if (project?.prefix) { + + /* filter based on prefix */ + if (base.is_meta && project?.prefix) { tables = tables.filter((t) => { return t?.tn?.startsWith(project?.prefix); }); } - */ + info.tablesCount = tables.length; @@ -297,7 +297,7 @@ async function populateMeta(base: Base, project: Project): Promise { }); /* filter based on prefix */ - if (project?.prefix) { + if (base.is_meta && project?.prefix) { views = tables.filter((t) => { return t?.tn?.startsWith(project?.prefix); }); From 121c4ed71c327ce863c1db49ac02480992426fc9 Mon Sep 17 00:00:00 2001 From: mertmit Date: Sat, 24 Sep 2022 18:33:42 +0300 Subject: [PATCH 023/108] fix: show only tables within same base as child table for LTAR Signed-off-by: mertmit --- .../smartsheet/column/LinkedToAnotherRecordOptions.vue | 2 +- .../nc-gui/components/smartsheet/column/LookupOptions.vue | 2 +- .../nc-gui/components/smartsheet/column/RollupOptions.vue | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue b/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue index e5840621e9..f137ed3edd 100644 --- a/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue @@ -44,7 +44,7 @@ const refTables = $computed(() => { return [] } - return tables.filter((t) => t.type === ModelTypes.TABLE) + return tables.filter((t) => t.type === ModelTypes.TABLE && t.base_id === meta?.base_id) }) const filterOption = (value: string, option: { key: string }) => option.key.toLowerCase().includes(value.toLowerCase()) diff --git a/packages/nc-gui/components/smartsheet/column/LookupOptions.vue b/packages/nc-gui/components/smartsheet/column/LookupOptions.vue index 77279ae9f1..0667cc7b9f 100644 --- a/packages/nc-gui/components/smartsheet/column/LookupOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/LookupOptions.vue @@ -34,7 +34,7 @@ const refTables = $computed(() => { } const _refTables = meta.columns - .filter((column) => column.uidt === UITypes.LinkToAnotherRecord && !column.system) + .filter((column) => column.uidt === UITypes.LinkToAnotherRecord && !column.system && column.base_id === meta?.base_id) .map((column) => ({ col: column.colOptions, column, diff --git a/packages/nc-gui/components/smartsheet/column/RollupOptions.vue b/packages/nc-gui/components/smartsheet/column/RollupOptions.vue index 960cbb7507..20e1a54333 100644 --- a/packages/nc-gui/components/smartsheet/column/RollupOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/RollupOptions.vue @@ -47,7 +47,13 @@ const refTables = $computed(() => { } const _refTables = meta.columns - .filter((c) => c.uidt === UITypes.LinkToAnotherRecord && (c.colOptions as LinkToAnotherRecordType).type !== 'bt' && !c.system) + .filter( + (c) => + c.uidt === UITypes.LinkToAnotherRecord && + (c.colOptions as LinkToAnotherRecordType).type !== 'bt' && + !c.system && + c.base_id === meta?.base_id, + ) .map((c) => ({ col: c.colOptions, column: c, From 5c433e3086283f35ff4e4da6aecb4133e8d5563b Mon Sep 17 00:00:00 2001 From: mertmit Date: Sun, 25 Sep 2022 16:43:16 +0300 Subject: [PATCH 024/108] feat: team & settings modal data sources tab revised Signed-off-by: mertmit --- packages/nc-gui/components.d.ts | 7 +- .../nc-gui/components/cell/DateTimePicker.vue | 4 +- .../nc-gui/components/cell/MultiSelect.vue | 2 +- .../nc-gui/components/cell/TimePicker.vue | 6 +- .../nc-gui/components/dashboard/TreeView.vue | 16 +- .../dashboard/settings/AppStore.vue | 169 +++++++++--------- .../dashboard/settings/DataSources.vue | 142 ++++++++++----- .../components/dashboard/settings/Erd.vue | 10 +- .../dashboard/settings/Metadata.vue | 10 +- .../components/dashboard/settings/Modal.vue | 153 +++++++++++----- .../components/dashboard/settings/UIAcl.vue | 11 +- .../nc-gui/components/dlg/TableRename.vue | 9 +- packages/nc-gui/components/erd/View.vue | 19 +- .../components/general/AddBaseButton.vue | 21 +++ .../smartsheet/column/CurrencyOptions.vue | 4 +- .../components/virtual-cell/Formula.vue | 2 +- .../composables/useColumnCreateStore.ts | 11 +- packages/nc-gui/composables/useProject.ts | 8 +- packages/nc-gui/context/index.ts | 1 + packages/nc-gui/lib/enums.ts | 9 + .../pages/[projectType]/[projectId]/index.vue | 13 +- .../nocodb/src/lib/meta/api/metaDiffApis.ts | 7 + 22 files changed, 421 insertions(+), 213 deletions(-) create mode 100644 packages/nc-gui/components/general/AddBaseButton.vue diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts index 1664e337f5..3af2f902d4 100644 --- a/packages/nc-gui/components.d.ts +++ b/packages/nc-gui/components.d.ts @@ -10,6 +10,8 @@ declare module '@vue/runtime-core' { AAlert: typeof import('ant-design-vue/es')['Alert'] AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete'] ABadgeRibbon: typeof import('ant-design-vue/es')['BadgeRibbon'] + ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] + ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] AButton: typeof import('ant-design-vue/es')['Button'] ACard: typeof import('ant-design-vue/es')['Card'] ACardMeta: typeof import('ant-design-vue/es')['CardMeta'] @@ -65,7 +67,6 @@ declare module '@vue/runtime-core' { ATabs: typeof import('ant-design-vue/es')['Tabs'] ATag: typeof import('ant-design-vue/es')['Tag'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] - ATimePicker: typeof import('ant-design-vue/es')['TimePicker'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle'] AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger'] @@ -144,7 +145,9 @@ declare module '@vue/runtime-core' { MdiContentSave: typeof import('~icons/mdi/content-save')['default'] MdiContentSaveEdit: typeof import('~icons/mdi/content-save-edit')['default'] MdiCurrencyUsd: typeof import('~icons/mdi/currency-usd')['default'] + MdiDatabaseLockOutline: typeof import('~icons/mdi/database-lock-outline')['default'] MdiDatabaseOutline: typeof import('~icons/mdi/database-outline')['default'] + MdiDatabasePlusOutline: typeof import('~icons/mdi/database-plus-outline')['default'] MdiDatabaseSync: typeof import('~icons/mdi/database-sync')['default'] MdiDelete: typeof import('~icons/mdi/delete')['default'] MdiDeleteOutline: typeof import('~icons/mdi/delete-outline')['default'] @@ -176,6 +179,7 @@ declare module '@vue/runtime-core' { MdiFunction: typeof import('~icons/mdi/function')['default'] MdiGestureDoubleTap: typeof import('~icons/mdi/gesture-double-tap')['default'] MdiGithub: typeof import('~icons/mdi/github')['default'] + MdiGraphOutline: typeof import('~icons/mdi/graph-outline')['default'] MdiHeart: typeof import('~icons/mdi/heart')['default'] MdiHook: typeof import('~icons/mdi/hook')['default'] MdiInformation: typeof import('~icons/mdi/information')['default'] @@ -193,6 +197,7 @@ declare module '@vue/runtime-core' { MdiMagnify: typeof import('~icons/mdi/magnify')['default'] MdiMenu: typeof import('~icons/mdi/menu')['default'] MdiMenuDown: typeof import('~icons/mdi/menu-down')['default'] + MdiMenuIcon: typeof import('~icons/mdi/menu-icon')['default'] MdiMicrosoftTeams: typeof import('~icons/mdi/microsoft-teams')['default'] MdiMinusCircleOutline: typeof import('~icons/mdi/minus-circle-outline')['default'] MdiMoonFull: typeof import('~icons/mdi/moon-full')['default'] diff --git a/packages/nc-gui/components/cell/DateTimePicker.vue b/packages/nc-gui/components/cell/DateTimePicker.vue index 8819d23ce7..8ad758b8b8 100644 --- a/packages/nc-gui/components/cell/DateTimePicker.vue +++ b/packages/nc-gui/components/cell/DateTimePicker.vue @@ -28,9 +28,11 @@ const active = inject(ActiveCellInj, ref(false)) const editable = inject(EditModeInj, ref(false)) +const column = inject(ColumnInj)! + let isDateInvalid = $ref(false) -const dateFormat = isMysql ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ' +const dateFormat = isMysql(column.value.base_id) ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ' let localState = $computed({ get() { diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 0e8ac91571..3815933e49 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -105,7 +105,7 @@ const vModel = computed({ const selectedTitles = computed(() => modelValue ? typeof modelValue === 'string' - ? isMysql + ? isMysql(column.value.base_id) ? modelValue.split(',').sort((a, b) => { const opa = options.value.find((el) => el.title === a) const opb = options.value.find((el) => el.title === b) diff --git a/packages/nc-gui/components/cell/TimePicker.vue b/packages/nc-gui/components/cell/TimePicker.vue index 6402de2bed..1d9f8fe7c6 100644 --- a/packages/nc-gui/components/cell/TimePicker.vue +++ b/packages/nc-gui/components/cell/TimePicker.vue @@ -1,6 +1,6 @@ diff --git a/packages/nc-gui/components/dashboard/settings/DataSources.vue b/packages/nc-gui/components/dashboard/settings/DataSources.vue index 8f4d227157..af8395cb3b 100644 --- a/packages/nc-gui/components/dashboard/settings/DataSources.vue +++ b/packages/nc-gui/components/dashboard/settings/DataSources.vue @@ -2,69 +2,90 @@ import { Empty } from 'ant-design-vue' import type { BaseType } from 'nocodb-sdk' import CreateBase from './data-sources/CreateBase.vue' +import Metadata from './Metadata.vue' +import UIAcl from './UIAcl.vue' +import Erd from './Erd.vue' +import { DataSourcesSubTab } from '~/lib' import { useNuxtApp, useProject } from '#imports' +interface Props { + state: string + reload: boolean +} + +const props = defineProps() + +const emits = defineEmits(['update:state', 'update:reload']) + +const vModel = useVModel(props, 'state', emits) +const vReload = useVModel(props, 'reload', emits) + const { $api } = useNuxtApp() const { project } = useProject() -let isLoading = $ref(false) let sources = $ref([]) -const newSourceTab = $ref(false) +let activeBaseId = $ref('') +let metadiffbases = $ref([]) async function loadBases() { try { if (!project.value?.id) return - isLoading = true + vReload.value = true const baseList = await $api.base.list(project.value?.id) if (baseList.bases.list && baseList.bases.list.length) { sources = baseList.bases.list } + loadMetaDiff() } catch (e) { console.error(e) } finally { - isLoading = false + vReload.value = false + } +} + +async function loadMetaDiff() { + try { + if (!project.value?.id) return + + metadiffbases = [] + + const metadiff = await $api.project.metaDiffGet(project.value?.id) + for (const model of metadiff) { + if (model.detectedChanges?.length > 0) { + metadiffbases.push(model.base_id) + } + } + } catch (e) { + console.error(e) } } +const baseAction = (baseId: string, action: string) => { + activeBaseId = baseId + vModel.value = action +} + onMounted(async () => { if (sources.length === 0) { await loadBases() } }) + +watch( + () => props.reload, + async (reload) => { + if (reload) { + await loadBases() + } + }, +) diff --git a/packages/nc-gui/components/dashboard/settings/Erd.vue b/packages/nc-gui/components/dashboard/settings/Erd.vue index e8ca1d8bd8..d89b7e8d1e 100644 --- a/packages/nc-gui/components/dashboard/settings/Erd.vue +++ b/packages/nc-gui/components/dashboard/settings/Erd.vue @@ -1,5 +1,13 @@ + + diff --git a/packages/nc-gui/components/dashboard/settings/Metadata.vue b/packages/nc-gui/components/dashboard/settings/Metadata.vue index e515eb9fb2..5d69142a4e 100644 --- a/packages/nc-gui/components/dashboard/settings/Metadata.vue +++ b/packages/nc-gui/components/dashboard/settings/Metadata.vue @@ -1,6 +1,12 @@