diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index d140b80a3a..388cb7c8a6 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -118,19 +118,19 @@ jobs: db: mysql shard: 2 playwright-mysql-3: - needs: playwright-mysql-1 - if: ${{ always() && ( github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft )}} + needs: pre-build-for-playwright + if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} uses: ./.github/workflows/playwright-test-workflow.yml with: db: mysql shard: 3 playwright-mysql-4: - needs: playwright-mysql-2 - if: ${{ always() && ( github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft )}} + needs: pre-build-for-playwright + if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} uses: ./.github/workflows/playwright-test-workflow.yml with: db: mysql - shard: 4 + shard: 4 playwright-sqlite-1: needs: pre-build-for-playwright if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} @@ -145,6 +145,20 @@ jobs: with: db: sqlite shard: 2 + playwright-sqlite-3: + needs: pre-build-for-playwright + if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} + uses: ./.github/workflows/playwright-test-workflow.yml + with: + db: sqlite + shard: 3 + playwright-sqlite-4: + needs: pre-build-for-playwright + if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} + uses: ./.github/workflows/playwright-test-workflow.yml + with: + db: sqlite + shard: 4 playwright-pg-shard-1: needs: pre-build-for-playwright if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} @@ -160,16 +174,16 @@ jobs: db: pg shard: 2 playwright-pg-shard-3: - needs: playwright-pg-shard-1 - if: ${{ always() && ( github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft )}} + needs: pre-build-for-playwright + if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} uses: ./.github/workflows/playwright-test-workflow.yml with: db: pg - shard: 3 + shard: 3 playwright-pg-shard-4: - needs: playwright-pg-shard-2 - if: ${{ always() && ( github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft ) }} + needs: pre-build-for-playwright + if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} uses: ./.github/workflows/playwright-test-workflow.yml with: db: pg - shard: 4 + shard: 4 \ No newline at end of file diff --git a/.github/workflows/playwright-test-workflow.yml b/.github/workflows/playwright-test-workflow.yml index 82830b94be..f753fec7f8 100644 --- a/.github/workflows/playwright-test-workflow.yml +++ b/.github/workflows/playwright-test-workflow.yml @@ -13,46 +13,36 @@ on: jobs: playwright: - runs-on: [self-hosted, v2] + runs-on: [self-hosted, v3] timeout-minutes: 100 steps: - name: Checkout uses: actions/checkout@v3 - - name: pre-requisite checks + - name: Check node,pnpm Installation and set Path shell: bash + working-directory: scripts/self-hosted-gh-runner + timeout-minutes: 1 run: | - node_version=$(node --version || echo "error") - pnpm_version=$(pnpm -v || echo "error") - echo "node version: $node_version" - echo "pnpm version: $pnpm_version" - if [[ $node_version != v18* ]] || [[ $pnp_version != 8* ]]; then - echo "version mismatch: expected node v18 and pnpm v8" - RUN_PRERQUISITE_STEPS="true" - elif [[ ${FORCE_RUN_PRERQUISITE_STEPS} == "true" ]]; - # || [[ ! -f ${PRE_REQ_CHECK_FILE_PATH} ]]; - then - echo "FORCE_RUN_PRERQUISITE_STEPS is true" - RUN_PRERQUISITE_STEPS="true" - else - RUN_PRERQUISITE_STEPS="false" - fi - echo "RUN_PRERQUISITE_STEPS=${RUN_PRERQUISITE_STEPS}" >> $GITHUB_ENV + ./node-pnpm-check.sh + echo "make sure below mentioned versions are expected versions" + echo "If you are expecting the node and pnpm versions to be updated. Please update the node-pnpm-check.sh script" + env - name: Setup Node - if: env.RUN_PRERQUISITE_STEPS == 'true' + if: ${{ env.SETUP_NODE != 'false' }} uses: actions/setup-node@v3 with: - node-version: 18.14.0 + node-version: ${{ env.NC_REQ_NODE_V }} - name: Setup pnpm - if: env.RUN_PRERQUISITE_STEPS == 'true' + if: ${{ env.SETUP_PNPM != 'false' }} uses: pnpm/action-setup@v2 with: - version: 8 + version: ${{ env.NC_REQ_PNPM_V }} - name: Get pnpm store directory shell: bash run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + echo "STORE_PATH=/root/setup-pnpm/node_modules/.bin/store/v3" >> $GITHUB_ENV - uses: actions/cache@v3 - if: env.RUN_PRERQUISITE_STEPS == 'true' + if: env.IS_NPM_CACHE_DOWNLOAD_REQUIRED == 'true' name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} @@ -113,22 +103,28 @@ jobs: working-directory: ./packages/nocodb run: | pnpm run watch:run:playwright:pg &> ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & - - name: copy built output and start frontend - if: always() - working-directory: ./ + - name: download ui build from artf server + working-directory: ./packages/nc-gui/ run: | # expects the variables to be available in runner context. - path="gh-artifacts/runs/${GITHUB_RUN_ID}/ui-build/.output" - target_dir="/mnt/${path}" - mkdir -p ${target_dir} - if [[ -d ${target_dir} ]]; then - echo "Directory ${target_dir} exists." - cp -r ${target_dir} ./packages/nc-gui/ || echo "playwright reports directory does not exists" >> ${target_dir}/playwright-report/index.html - else - echo "Error: Directory ${target_dir} does not exists." - exit 1 - fi - cd ./packages/nc-gui/ + rm -rf .output.zip .output + FILE="$(echo ${GITHUB_REPOSITORY} | sed "s,/,-,g")-${GITHUB_RUN_ID}.zip" + BUILD_FILE_URL=http://65.21.27.147/gh-artifacts/ui-builds/${FILE} + time curl -o output.zip ${BUILD_FILE_URL} -n + unzip -oq output.zip + + # path="gh-artifacts/runs/${GITHUB_RUN_ID}/ui-build/.output" + # target_dir="/mnt/${path}" + # if [[ -d ${target_dir} ]]; then + # echo "Directory ${target_dir} exists." + # cp -r ${target_dir} . || echo "playwright reports directory does not exists" >> ${target_dir}/playwright-report/index.html + # else + # echo "Error: Directory ${target_dir} does not exists." + # exit 1 + # fi + - name: start frontend + working-directory: ./packages/nc-gui/ + run: | pnpm run ci:start - name: Install Playwright Browsers working-directory: ./tests/playwright @@ -183,7 +179,8 @@ jobs: # - name: Run quick tests # if: ${{ inputs.db == 'sqlite' }} # working-directory: ./tests/playwright -# run: PLAYWRIGHT_HTML_REPORT=playwright-report-quick npm run test:quick +# run: PLAYWRIGHT_HTML_REPORT=playwright-report-quick npm run test:quick + - uses: actions/upload-artifact@v3 if: ${{ inputs.db == 'sqlite' }} with: @@ -234,6 +231,6 @@ jobs: cp -r ./tests/playwright/playwright-report ${target_dir}/ || echo "playwright reports directory does not exists" >> ${target_dir}/playwright-report/index.html cp ./packages/nocodb/*_test_backend.log ${target_dir}/ || echo "backend logs file does not exists" >> ${target_dir}/index.html # end: artifacts copy - SUMMARY='[Artifacts](http://135.181.48.96/'${path}') - [playwright-report](http://135.181.48.96/'${path}'/playwright-report)' + SUMMARY='[Artifacts]('${REPORTS_HOST:-http://135.181.48.96}/${path}') + [playwright-report]('${REPORTS_HOST:-http://135.181.48.96}/${path}'/playwright-report)' echo "$SUMMARY" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/pre-build-for-playwright.yml b/.github/workflows/pre-build-for-playwright.yml index 520f3b74d8..079137dfff 100644 --- a/.github/workflows/pre-build-for-playwright.yml +++ b/.github/workflows/pre-build-for-playwright.yml @@ -11,46 +11,36 @@ on: jobs: playwright: - runs-on: [self-hosted, aws] + runs-on: [self-hosted, v3] timeout-minutes: 100 steps: - name: Checkout uses: actions/checkout@v3 - - name: pre-requisite checks + - name: Check node,pnpm Installation and set Path shell: bash + working-directory: scripts/self-hosted-gh-runner + timeout-minutes: 1 run: | - node_version=$(node --version || echo "error") - pnpm_version=$(pnpm -v || echo "error") - echo "node version: $node_version" - echo "pnpm version: $pnpm_version" - if [[ $node_version != v18* ]] || [[ $pnp_version != 8* ]]; then - echo "version mismatch: expected node v18 and pnpm v8" - RUN_PRERQUISITE_STEPS="true" - elif [[ ${FORCE_RUN_PRERQUISITE_STEPS} == "true" ]]; - # || [[ ! -f ${PRE_REQ_CHECK_FILE_PATH} ]]; - then - echo "FORCE_RUN_PRERQUISITE_STEPS is true" - RUN_PRERQUISITE_STEPS="true" - else - RUN_PRERQUISITE_STEPS="false" - fi - echo "RUN_PRERQUISITE_STEPS=${RUN_PRERQUISITE_STEPS}" >> $GITHUB_ENV + ./node-pnpm-check.sh + echo "make sure below mentioned versions are expected versions" + echo "If you are expecting the node and pnpm versions to be updated. Please update the node-pnpm-check.sh script" + env - name: Setup Node - if: env.RUN_PRERQUISITE_STEPS == 'true' + if: ${{ env.SETUP_NODE != 'false' }} uses: actions/setup-node@v3 with: - node-version: 18.14.0 + node-version: ${{ env.NC_REQ_NODE_V }} - name: Setup pnpm - if: env.RUN_PRERQUISITE_STEPS == 'true' + if: ${{ env.SETUP_PNPM != 'false' }} uses: pnpm/action-setup@v2 with: - version: 8 + version: ${{ env.NC_REQ_PNPM_V }} - name: Get pnpm store directory shell: bash run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + echo "STORE_PATH=/root/setup-pnpm/node_modules/.bin/store/v3" >> $GITHUB_ENV - uses: actions/cache@v3 - if: env.RUN_PRERQUISITE_STEPS == 'true' + if: env.IS_NPM_CACHE_DOWNLOAD_REQUIRED == 'true' name: Setup pnpm cache with: path: ${{ env.STORE_PATH }} @@ -59,22 +49,18 @@ jobs: ${{ runner.os }}-pnpm-store- - name: install dependencies run: pnpm bootstrap - - name: run frontend + - name: build frontend (nc-gui) working-directory: ./packages/nc-gui run: | pnpm run build timeout-minutes: 20 - - name: Copy Artifacts to Local Artifacts Dir - if: always() + - name: upload frontend (nc-gui) build to artf server working-directory: ./ run: | # expects the variables to be available in runner context. - path="gh-artifacts/runs/${GITHUB_RUN_ID}/ui-build/" - target_dir="/mnt/${path}" - mkdir -p ${target_dir} - - # start : add any artifacts to be copied here - cp -r ./packages/nc-gui/.output ${target_dir}/ || echo "playwright reports directory does not exists" >> ${target_dir}/playwright-report/index.html - - SUMMARY='[UI BUILD](http://65.21.27.147/'${path}')' - echo "$SUMMARY" >> $GITHUB_STEP_SUMMARY + FILE="$(echo ${GITHUB_REPOSITORY} | sed "s,/,-,g")-${GITHUB_RUN_ID}.zip" + cd ./packages/nc-gui/ + zip -r ${FILE} .output || echo "UI build directory does not exists" >&2 + echo "uploading ${FILE} to http://65.21.27.147/upload/${FILE}" + time curl -T "${FILE}" http://65.21.27.147/upload/${FILE} -n + diff --git a/.github/workflows/publish-docs-index-typesense.yml b/.github/workflows/publish-docs-index-typesense.yml new file mode 100644 index 0000000000..6b4aedf3b8 --- /dev/null +++ b/.github/workflows/publish-docs-index-typesense.yml @@ -0,0 +1,26 @@ +name: "Publish : Docs search index (Typesense)" + +on: + # Triggered manually + workflow_dispatch: + +jobs: + doc-indexer: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 # You MUST checkout your repository first! + + - name: Run DocSearch Scraper + uses: celsiusnarhwal/typesense-scraper@v2 + with: + # The secret containing your Typesense API key. Required. + api-key: ${{ secrets.TYPESENSE_API_KEY }} + # The hostname or IP address of your Typesense server. Required. + host: ${{ secrets.TYPESENSE_HOST }} + # The port on which your Typesense server is listening. Optional. Default: 8108. + port: 443 + # The protocol to use when connecting to your Typesense server. Optional. Default: http. + protocol: https + # The path to your DocSearch config file. Optional. Default: docsearch.config.json. + config: ./packages/noco-docs/typesense-scrape-config.json \ No newline at end of file diff --git a/packages/nc-gui/assets/img/brand/nocodb.png b/packages/nc-gui/assets/img/brand/nocodb.png new file mode 100644 index 0000000000..188ed5e198 Binary files /dev/null and b/packages/nc-gui/assets/img/brand/nocodb.png differ diff --git a/packages/nc-gui/assets/img/fieldPlaceholder.svg b/packages/nc-gui/assets/img/fieldPlaceholder.svg new file mode 100644 index 0000000000..7fc8286651 --- /dev/null +++ b/packages/nc-gui/assets/img/fieldPlaceholder.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/add-data-source.svg b/packages/nc-gui/assets/nc-icons/add-data-source.svg new file mode 100644 index 0000000000..6ea287bc88 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/add-data-source.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/bt-solid.svg b/packages/nc-gui/assets/nc-icons/bt-solid.svg new file mode 100644 index 0000000000..d5a0e362c2 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/bt-solid.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/comment_here.svg b/packages/nc-gui/assets/nc-icons/comment_here.svg new file mode 100644 index 0000000000..1b413ad9ea --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/comment_here.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/nc-gui/assets/nc-icons/commentor.svg b/packages/nc-gui/assets/nc-icons/commentor.svg new file mode 100644 index 0000000000..fee40f383d --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/commentor.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/creator.svg b/packages/nc-gui/assets/nc-icons/creator.svg new file mode 100644 index 0000000000..954e553b38 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/creator.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/download.svg b/packages/nc-gui/assets/nc-icons/download.svg index 2692f7d8fb..2403b35d0e 100644 --- a/packages/nc-gui/assets/nc-icons/download.svg +++ b/packages/nc-gui/assets/nc-icons/download.svg @@ -1,5 +1,5 @@ - + diff --git a/packages/nc-gui/assets/nc-icons/editor.svg b/packages/nc-gui/assets/nc-icons/editor.svg new file mode 100644 index 0000000000..e9190dd7b9 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/editor.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/hm-solid.svg b/packages/nc-gui/assets/nc-icons/hm-solid.svg new file mode 100644 index 0000000000..efe6772786 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/hm-solid.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/lookup.svg b/packages/nc-gui/assets/nc-icons/lookup.svg new file mode 100644 index 0000000000..86a84f7859 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/lookup.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/mm-solid.svg b/packages/nc-gui/assets/nc-icons/mm-solid.svg new file mode 100644 index 0000000000..eaa045334a --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/mm-solid.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/no-access.svg b/packages/nc-gui/assets/nc-icons/no-access.svg new file mode 100644 index 0000000000..0f301628e0 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/no-access.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/owner.svg b/packages/nc-gui/assets/nc-icons/owner.svg new file mode 100644 index 0000000000..c2ad9a442f --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/owner.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/project.svg b/packages/nc-gui/assets/nc-icons/project.svg new file mode 100644 index 0000000000..92fb62f59c --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/project.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/record.svg b/packages/nc-gui/assets/nc-icons/record.svg new file mode 100644 index 0000000000..7515f62243 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/record.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/nc-icons/table.svg b/packages/nc-gui/assets/nc-icons/table.svg new file mode 100644 index 0000000000..d786170f50 --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/table.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/nc-gui/assets/nc-icons/users.svg b/packages/nc-gui/assets/nc-icons/users.svg index 60c1c870fb..2e75180b88 100644 --- a/packages/nc-gui/assets/nc-icons/users.svg +++ b/packages/nc-gui/assets/nc-icons/users.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/packages/nc-gui/assets/nc-icons/viewer.svg b/packages/nc-gui/assets/nc-icons/viewer.svg new file mode 100644 index 0000000000..c2ad9a442f --- /dev/null +++ b/packages/nc-gui/assets/nc-icons/viewer.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss index 9ed30f0f35..cbeb575b6e 100644 --- a/packages/nc-gui/assets/style.scss +++ b/packages/nc-gui/assets/style.scss @@ -48,10 +48,19 @@ main { @apply m-0 h-full w-full bg-white; } + .nc-input-md { @apply !rounded-lg !py-2 !px-3 mb-1; } +.mobile { + .nc-scrollbar-md, nc-scrollbar-dark-md, nc-scrollbar-dark-md, nc-scrollbar-sm-dark, nc-scrollbar-x-md { + &::-webkit-scrollbar { + width: 0px; + } + } +} + .nc-scrollbar-md { overflow-y: scroll; overflow-x: hidden; @@ -177,14 +186,14 @@ a { // menu item styling .nc-menu-item { - @apply cursor-pointer text-xs flex items-center gap-2 px-4 py-3 relative after:(content-[''] absolute top-0 left-0 bottom-0 w-full h-full right-0 bg-current opacity-0 transition transition-opactity duration-100) hover:(after:(opacity-5)); + @apply cursor-pointer text-sm flex items-center gap-2 px-4 py-3 relative after:(content-[''] absolute top-0 left-0 bottom-0 w-full h-full right-0 bg-current opacity-0 transition transition-opactity duration-100) hover:(after:(opacity-5)); &.disabled { @apply text-black text-opacity-25 bg-[#f5f5f5] cursor-not-allowed text-shadow-none box-shadow-none border-[#d9d9d9]; } } -.nc-project-menu-item { +.nc-base-menu-item { @apply cursor-pointer flex items-center gap-2 py-2 after:(content-[''] absolute top-0 left-0 bottom-0 right-0 w-full h-full bg-current opacity-0 transition transition-opacity duration-100) hover:(after:(opacity-5)); // &:hover { @@ -433,9 +442,6 @@ a { @apply !shadow-none rounded ring-1 ring-red-600; } -.ant-modal { - @apply !top-[50px]; -} .ant-modal-content { @apply !p-6; border-radius: 1rem; @@ -596,6 +602,34 @@ input[type='number'] { @apply !block !py-1.5; } +.nc-sidebar-node { + @apply !xs:(min-h-12 max-h-12 hover:bg-gray-50 ml-1.5); + + .nc-emoji { + @apply xs:(text-lg); + } + .material-symbols, .nc-icon { + @apply !xs:(text-xl -mt-0.25); + } + + .nc-sidebar-node-title { + @apply xs:(text-base); + } + + .nc-sidebar-node-btn:not(.nc-sidebar-expand) { + @apply !xs:(hidden) + } +} + .nc-button.ant-btn.nc-sidebar-node-btn { @apply opacity-0 group-hover:(opacity-100) text-gray-600 hover:(bg-gray-400 bg-opacity-20 text-gray-900) duration-100; } + + +.nc-button.ant-btn.nc-sidebar-node-btn.nc-sidebar-expand { + @apply xs:(opacity-100 hover:bg-gray-50); + + .nc-icon { + @apply xs:(visible opacity-100 !text-gray-500) + } +} diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts index 0e78a9c6a6..28575de95a 100644 --- a/packages/nc-gui/components.d.ts +++ b/packages/nc-gui/components.d.ts @@ -91,6 +91,7 @@ declare module '@vue/runtime-core' { MaterialSymbolsChevronRightRounded: typeof import('~icons/material-symbols/chevron-right-rounded')['default'] MaterialSymbolsCloseRounded: typeof import('~icons/material-symbols/close-rounded')['default'] MaterialSymbolsDarkModeOutline: typeof import('~icons/material-symbols/dark-mode-outline')['default'] + MaterialSymbolsDeleteOutlineRounded: typeof import('~icons/material-symbols/delete-outline-rounded')['default'] MaterialSymbolsFileCopyOutline: typeof import('~icons/material-symbols/file-copy-outline')['default'] MaterialSymbolsKeyboardArrowDownRounded: typeof import('~icons/material-symbols/keyboard-arrow-down-rounded')['default'] MaterialSymbolsKeyboardReturn: typeof import('~icons/material-symbols/keyboard-return')['default'] @@ -106,7 +107,6 @@ declare module '@vue/runtime-core' { MdiAccount: typeof import('~icons/mdi/account')['default'] MdiAccountCircleOutline: typeof import('~icons/mdi/account-circle-outline')['default'] MdiAccountSupervisorOutline: typeof import('~icons/mdi/account-supervisor-outline')['default'] - MdiAlpha: typeof import('~icons/mdi/alpha')['default'] MdiAppleKeyboardShift: typeof import('~icons/mdi/apple-keyboard-shift')['default'] MdiArrowDownDropCircle: typeof import('~icons/mdi/arrow-down-drop-circle')['default'] MdiArrowDownDropCircleOutline: typeof import('~icons/mdi/arrow-down-drop-circle-outline')['default'] @@ -117,11 +117,10 @@ declare module '@vue/runtime-core' { MdiCardsHeart: typeof import('~icons/mdi/cards-heart')['default'] MdiCellphoneMessage: typeof import('~icons/mdi/cellphone-message')['default'] MdiChat: typeof import('~icons/mdi/chat')['default'] - MdiChatProcessingOutline: typeof import('~icons/mdi/chat-processing-outline')['default'] MdiCheck: typeof import('~icons/mdi/check')['default'] - MdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] MdiChevronLeft: typeof import('~icons/mdi/chevron-left')['default'] MdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] + MdiChevronUp: typeof import('~icons/mdi/chevron-up')['default'] MdiCircleMedium: typeof import('~icons/mdi/circle-medium')['default'] MdiClose: typeof import('~icons/mdi/close')['default'] MdiCodeTags: typeof import('~icons/mdi/code-tags')['default'] @@ -129,10 +128,13 @@ declare module '@vue/runtime-core' { MdiCurrencyUsd: typeof import('~icons/mdi/currency-usd')['default'] MdiDiscord: typeof import('~icons/mdi/discord')['default'] MdiDotsHorizontal: typeof import('~icons/mdi/dots-horizontal')['default'] + MdiDotsVertical: typeof import('~icons/mdi/dots-vertical')['default'] MdiEye: typeof import('~icons/mdi/eye')['default'] MdiFileDocumentMultipleOutline: typeof import('~icons/mdi/file-document-multiple-outline')['default'] + MdiFileDocumentOutline: typeof import('~icons/mdi/file-document-outline')['default'] MdiFlag: typeof import('~icons/mdi/flag')['default'] MdiHeart: typeof import('~icons/mdi/heart')['default'] + MdiHistory: typeof import('~icons/mdi/history')['default'] MdiKeyStar: typeof import('~icons/mdi/key-star')['default'] MdiLinkVariant: typeof import('~icons/mdi/link-variant')['default'] MdiLoading: typeof import('~icons/mdi/loading')['default'] @@ -141,6 +143,7 @@ declare module '@vue/runtime-core' { MdiMagnify: typeof import('~icons/mdi/magnify')['default'] MdiMapMarkerOutline: typeof import('~icons/mdi/map-marker-outline')['default'] MdiMenuDown: typeof import('~icons/mdi/menu-down')['default'] + MdiMessageOutline: typeof import('~icons/mdi/message-outline')['default'] MdiMicrosoftTeams: typeof import('~icons/mdi/microsoft-teams')['default'] MdiMoonFull: typeof import('~icons/mdi/moon-full')['default'] MdiPlus: typeof import('~icons/mdi/plus')['default'] @@ -159,9 +162,7 @@ declare module '@vue/runtime-core' { MdiWhatsapp: typeof import('~icons/mdi/whatsapp')['default'] MiCircleWarning: typeof import('~icons/mi/circle-warning')['default'] NcIconsInbox: typeof import('~icons/nc-icons/inbox')['default'] - PhLink: typeof import('~icons/ph/link')['default'] PhMagnifyingGlassBold: typeof import('~icons/ph/magnifying-glass-bold')['default'] - PhTriangleFill: typeof import('~icons/ph/triangle-fill')['default'] RiExternalLinkLine: typeof import('~icons/ri/external-link-line')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] diff --git a/packages/nc-gui/components/account/License.vue b/packages/nc-gui/components/account/License.vue index 795fb16e8c..e4d780bc17 100644 --- a/packages/nc-gui/components/account/License.vue +++ b/packages/nc-gui/components/account/License.vue @@ -5,6 +5,8 @@ import { extractSdkResponseErrorMsg, useApi, useGlobal } from '#imports' const { api, isLoading } = useApi() +const { t } = useI18n() + const { $e } = useNuxtApp() const { loadAppInfo } = useGlobal() @@ -22,7 +24,7 @@ const loadLicense = async () => { const setLicense = async () => { try { await api.orgLicense.set({ key: key.value }) - message.success('License key updated') + message.success(t('success.licenseKeyUpdated')) await loadAppInfo() } catch (e: any) { message.error(await extractSdkResponseErrorMsg(e)) @@ -35,14 +37,14 @@ loadLicense() diff --git a/packages/nc-gui/components/account/Profile.vue b/packages/nc-gui/components/account/Profile.vue index a1b338c0ca..ab5406de74 100644 --- a/packages/nc-gui/components/account/Profile.vue +++ b/packages/nc-gui/components/account/Profile.vue @@ -1,6 +1,8 @@ diff --git a/packages/nc-gui/components/account/Token.vue b/packages/nc-gui/components/account/Token.vue index 8bc05c633e..049495e54a 100644 --- a/packages/nc-gui/components/account/Token.vue +++ b/packages/nc-gui/components/account/Token.vue @@ -1,8 +1,8 @@ - - diff --git a/packages/nc-gui/components/account/UserList.vue b/packages/nc-gui/components/account/UserList.vue index 0b0ce5250a..cbffbbdfc8 100644 --- a/packages/nc-gui/components/account/UserList.vue +++ b/packages/nc-gui/components/account/UserList.vue @@ -1,11 +1,14 @@ + + diff --git a/packages/nc-gui/components/account/UsersModal.vue b/packages/nc-gui/components/account/UsersModal.vue index 2ea494980e..489508d9f8 100644 --- a/packages/nc-gui/components/account/UsersModal.vue +++ b/packages/nc-gui/components/account/UsersModal.vue @@ -79,7 +79,7 @@ const copyUrl = async () => { try { await copy(inviteUrl.value) - // Copied shareable base url to clipboard! + // Copied shareable source url to clipboard! message.success(t('msg.success.shareableURLCopied')) } catch (e: any) { message.error(e.message) @@ -124,7 +124,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
-
Copy Invite Token
+
{{ $t('activity.copyInviteToken') }}
diff --git a/packages/nc-gui/components/api-client/Headers.vue b/packages/nc-gui/components/api-client/Headers.vue index ad4f826526..9b58046552 100644 --- a/packages/nc-gui/components/api-client/Headers.vue +++ b/packages/nc-gui/components/api-client/Headers.vue @@ -66,10 +66,10 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase -
Header Name
+
{{ $t('labels.headerName') }}
-
Value
+
{{ $t('placeholder.value') }}
@@ -87,9 +87,9 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase @@ -97,7 +97,11 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase - + @@ -120,7 +124,7 @@ const filterOption = (input: string, option: Option) => option.value.toUpperCase
-
Add Header
+
{{ $t('labels.addHeader') }}
diff --git a/packages/nc-gui/components/api-client/Params.vue b/packages/nc-gui/components/api-client/Params.vue index 61aa3f1f2c..66a2e79336 100644 --- a/packages/nc-gui/components/api-client/Params.vue +++ b/packages/nc-gui/components/api-client/Params.vue @@ -24,11 +24,11 @@ const deleteParamRow = (i: number) => { -
Parameter Name
+
{{ $t('title.parameterName') }}
-
Value
+
{{ $t('placeholder.value') }}
@@ -41,13 +41,13 @@ const deleteParamRow = (i: number) => { - + - + @@ -69,7 +69,7 @@ const deleteParamRow = (i: number) => {
-
Add Parameter
+
{{ $t('activity.addParameter') }}
diff --git a/packages/nc-gui/components/cell/Checkbox.vue b/packages/nc-gui/components/cell/Checkbox.vue index 5445c3e983..c0bdd2fe73 100644 --- a/packages/nc-gui/components/cell/Checkbox.vue +++ b/packages/nc-gui/components/cell/Checkbox.vue @@ -8,7 +8,7 @@ import { getMdiIcon, inject, parseProp, - useProject, + useBase, useSelectedCellKeyupListener, } from '#imports' @@ -28,7 +28,7 @@ const emits = defineEmits() const active = inject(ActiveCellInj, ref(false)) -const { isMssql } = useProject() +const { isMssql } = useBase() const column = inject(ColumnInj) @@ -53,7 +53,7 @@ const checkboxMeta = computed(() => { const vModel = computed({ get: () => !!props.modelValue && props.modelValue !== '0' && props.modelValue !== 0 && props.modelValue !== 'false', - set: (val: any) => emits('update:modelValue', isMssql(column?.value?.base_id) ? +val : val), + set: (val: any) => emits('update:modelValue', isMssql(column?.value?.source_id) ? +val : val), }) function onClick(force?: boolean, event?: MouseEvent) { @@ -80,7 +80,7 @@ useSelectedCellKeyupListener(active, (e) => { diff --git a/packages/nc-gui/components/cell/Currency.vue b/packages/nc-gui/components/cell/Currency.vue index fc67d9274a..1b5d363969 100644 --- a/packages/nc-gui/components/cell/Currency.vue +++ b/packages/nc-gui/components/cell/Currency.vue @@ -79,7 +79,7 @@ onMounted(() => { v-model="vModel" type="number" class="w-full h-full text-sm border-none rounded-md outline-none" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" @blur="submitCurrency" @keydown.down.stop @keydown.left.stop @@ -93,7 +93,7 @@ onMounted(() => { @contextmenu.stop /> - NULL + {{ $t('general.null') }} {{ currency }} diff --git a/packages/nc-gui/components/cell/DatePicker.vue b/packages/nc-gui/components/cell/DatePicker.vue index 991fa52903..e1f2f79433 100644 --- a/packages/nc-gui/components/cell/DatePicker.vue +++ b/packages/nc-gui/components/cell/DatePicker.vue @@ -25,6 +25,8 @@ const { modelValue, isPk } = defineProps() const emit = defineEmits(['update:modelValue']) +const { t } = useI18n() + const { showNull } = useGlobal() const columnMeta = inject(ColumnInj, null)! @@ -84,11 +86,11 @@ watch( const placeholder = computed(() => { if (isEditColumn.value && (modelValue === '' || modelValue === null)) { - return '(Optional)' + return t('labels.optional') } else if (modelValue === null && showNull.value) { - return 'NULL' + return t('general.null') } else if (isDateInvalid.value) { - return 'Invalid date' + return t('msg.invalidDate') } else { return '' } diff --git a/packages/nc-gui/components/cell/DateTimePicker.vue b/packages/nc-gui/components/cell/DateTimePicker.vue index c0a3960d23..984806908e 100644 --- a/packages/nc-gui/components/cell/DateTimePicker.vue +++ b/packages/nc-gui/components/cell/DateTimePicker.vue @@ -13,7 +13,7 @@ import { parseProp, ref, timeFormats, - useProject, + useBase, useSelectedCellKeyupListener, watch, } from '#imports' @@ -28,7 +28,7 @@ const { modelValue, isPk, isUpdatedFromCopyNPaste } = defineProps() const emit = defineEmits(['update:modelValue']) -const { isMssql, isXcdbBase } = useProject() +const { isMssql, isXcdbBase } = useBase() const { showNull } = useGlobal() @@ -40,6 +40,8 @@ const editable = inject(EditModeInj, ref(false)) const isLockedMode = inject(IsLockedInj, ref(false)) +const { t } = useI18n() + const isEditColumn = inject(EditColumnInj, ref(false)) const column = inject(ColumnInj)! @@ -65,7 +67,7 @@ const localState = computed({ return undefined } - const isXcDB = isXcdbBase(column.value.base_id) + const isXcDB = isXcdbBase(column.value.source_id) // cater copy and paste // when copying a datetime cell, the copied value would be local time @@ -81,7 +83,7 @@ const localState = computed({ return /^\d+$/.test(modelValue) ? dayjs(+modelValue) : dayjs(modelValue) } - if (isMssql(column.value.base_id)) { + if (isMssql(column.value.source_id)) { // e.g. 2023-04-29T11:41:53.000Z return dayjs(modelValue) } @@ -137,11 +139,11 @@ watch( const placeholder = computed(() => { if (isEditColumn.value && (modelValue === '' || modelValue === null)) { - return '(Optional)' + return t('labels.optional') } else if (modelValue === null && showNull.value) { - return 'NULL' + return t('general.null') } else if (isDateInvalid.value) { - return 'Invalid date' + return t('msg.invalidDate') } else { return '' } diff --git a/packages/nc-gui/components/cell/Decimal.vue b/packages/nc-gui/components/cell/Decimal.vue index 547f14a227..80a7e9fc01 100644 --- a/packages/nc-gui/components/cell/Decimal.vue +++ b/packages/nc-gui/components/cell/Decimal.vue @@ -97,7 +97,7 @@ watch(isExpandedFormOpen, () => { class="outline-none !py-2 !px-1 border-none rounded-md w-full h-full !text-sm" type="number" :step="precision" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" style="letter-spacing: 0.06rem" @blur="editEnabled = false" @keydown.down.stop="onKeyDown" @@ -110,7 +110,7 @@ watch(isExpandedFormOpen, () => { @selectstart.capture.stop @mousedown.stop /> - NULL + {{ $t('general.null') }} {{ displayValue }} diff --git a/packages/nc-gui/components/cell/Duration.vue b/packages/nc-gui/components/cell/Duration.vue index 34cc923bf1..c3bc7bb3ea 100644 --- a/packages/nc-gui/components/cell/Duration.vue +++ b/packages/nc-gui/components/cell/Duration.vue @@ -23,6 +23,8 @@ const { modelValue, showValidationError = true } = defineProps() const emit = defineEmits(['update:modelValue']) +const { t } = useI18n() + const { showNull } = useGlobal() const column = inject(ColumnInj) @@ -39,7 +41,9 @@ const isEditColumn = inject(EditColumnInj, ref(false)) const durationType = computed(() => parseProp(column?.value?.meta)?.duration || 0) -const durationPlaceholder = computed(() => (isEditColumn.value ? '(Optional)' : durationOptions[durationType.value].title)) +const durationPlaceholder = computed(() => + isEditColumn.value ? `(${t('labels.optional')})` : durationOptions[durationType.value].title, +) const localState = computed({ get: () => convertMS2Duration(modelValue, durationType.value), @@ -105,13 +109,12 @@ const focus: VNodeRef = (el) => !isExpandedFormOpen.value && !isEditColumn.value @mousedown.stop /> - NULL + {{ $t('general.null') }} {{ localState }}
- - Please enter a number + {{ $t('msg.plsEnterANumber') }}
diff --git a/packages/nc-gui/components/cell/Email.vue b/packages/nc-gui/components/cell/Email.vue index 434dd0a1fc..385fecb852 100644 --- a/packages/nc-gui/components/cell/Email.vue +++ b/packages/nc-gui/components/cell/Email.vue @@ -71,7 +71,7 @@ watch( :ref="focus" v-model="vModel" class="w-full outline-none text-sm px-1 py-2" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" @blur="editEnabled = false" @keydown.down.stop @keydown.left.stop @@ -84,7 +84,7 @@ watch( @mousedown.stop /> - NULL + {{ $t('general.null') }} !isExpandedFormOpen.value && !isEditColumn.value class="outline-none px-1 border-none w-full h-full text-sm" type="number" step="0.1" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" @blur="editEnabled = false" @keydown.down.stop @keydown.left.stop @@ -63,7 +63,7 @@ const focus: VNodeRef = (el) => !isExpandedFormOpen.value && !isEditColumn.value @selectstart.capture.stop @mousedown.stop /> - NULL + {{ $t('general.null') }} {{ vModel }} diff --git a/packages/nc-gui/components/cell/Integer.vue b/packages/nc-gui/components/cell/Integer.vue index 477232670a..5c0bf8944a 100644 --- a/packages/nc-gui/components/cell/Integer.vue +++ b/packages/nc-gui/components/cell/Integer.vue @@ -87,8 +87,11 @@ function onKeyDown(e: any) { v-model="vModel" class="outline-none py-2 px-1 border-none w-full h-full text-sm" type="number" + :class="{ + 'pl-2': isExpandedFormOpen, + }" style="letter-spacing: 0.06rem" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" @blur="editEnabled = false" @keydown="onKeyDown" @keydown.down.stop @@ -99,7 +102,7 @@ function onKeyDown(e: any) { @selectstart.capture.stop @mousedown.stop /> - NULL + {{ $t('general.null') }} {{ displayValue }} diff --git a/packages/nc-gui/components/cell/Json.vue b/packages/nc-gui/components/cell/Json.vue index 1206b4b5b2..fa28ad10a0 100644 --- a/packages/nc-gui/components/cell/Json.vue +++ b/packages/nc-gui/components/cell/Json.vue @@ -1,9 +1,10 @@ diff --git a/packages/nc-gui/components/cell/PhoneNumber.vue b/packages/nc-gui/components/cell/PhoneNumber.vue index 42f2f37f25..24c3c8ef36 100644 --- a/packages/nc-gui/components/cell/PhoneNumber.vue +++ b/packages/nc-gui/components/cell/PhoneNumber.vue @@ -15,6 +15,8 @@ const rowHeight = inject(RowHeightInj, ref(undefined)) const { showNull } = useGlobal() +const { t } = useI18n() + const editEnabled = inject(EditModeInj)! const isEditColumn = inject(EditColumnInj, ref(false)) @@ -46,7 +48,7 @@ watch( () => editEnabled.value, () => { if (parseProp(column.value.meta)?.validate && !editEnabled.value && localState.value && !isMobilePhone(localState.value)) { - message.error('Invalid Phone Number') + message.error(t('msg.invalidPhoneNumber')) localState.value = undefined return } @@ -61,7 +63,7 @@ watch( :ref="focus" v-model="vModel" class="w-full outline-none text-sm px-1 py-2" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" @blur="editEnabled = false" @keydown.down.stop @keydown.left.stop @@ -74,7 +76,7 @@ watch( @mousedown.stop /> - NULL + {{ $t('general.null') }} diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index 4e357d1cb2..f671553f7c 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -20,8 +20,8 @@ import { inject, isDrawerOrModalExist, ref, + useBase, useEventListener, - useProject, useRoles, useSelectedCellKeyupListener, watch, @@ -71,7 +71,7 @@ const { getMeta } = useMetas() const { isUIAllowed } = useRoles() -const { isPg, isMysql } = useProject() +const { isPg, isMysql } = useBase() // a variable to keep newly created option value // temporary until it's add the option to column meta @@ -175,7 +175,7 @@ async function addIfMissingAndSave() { // todo: refactor and avoid repetition if (updatedColMeta.cdf) { // Postgres returns default value wrapped with single quotes & casted with type so we have to get value between single quotes to keep it unified for all databases - if (isPg(column.value.base_id)) { + if (isPg(column.value.source_id)) { updatedColMeta.cdf = updatedColMeta.cdf.substring( updatedColMeta.cdf.indexOf(`'`) + 1, updatedColMeta.cdf.lastIndexOf(`'`), @@ -183,7 +183,7 @@ async function addIfMissingAndSave() { } // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped - if (!isMysql(column.value.base_id)) { + if (!isMysql(column.value.source_id)) { updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") } } @@ -217,6 +217,7 @@ const onKeydown = (e: KeyboardEvent) => { const onSelect = () => { isOpen.value = false + isEditable.value = false } const cellClickHook = inject(CellClickHookInj, null) @@ -286,7 +287,7 @@ const selectedOpt = computed(() => { v-model:value="vModel" class="w-full overflow-hidden" :class="{ 'caret-transparent': !hasEditRoles }" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" :allow-clear="!column.rqd && editAllowed" :bordered="false" :open="isOpen && editAllowed" @@ -324,7 +325,7 @@ const selectedOpt = computed(() => {
- Create new option named {{ searchVal }} + {{ $t('msg.selectOption.createNewOptionNamed') }} {{ searchVal }}
diff --git a/packages/nc-gui/components/cell/Text.vue b/packages/nc-gui/components/cell/Text.vue index 9fc0b9209e..e3ec8698a6 100644 --- a/packages/nc-gui/components/cell/Text.vue +++ b/packages/nc-gui/components/cell/Text.vue @@ -33,7 +33,10 @@ const focus: VNodeRef = (el) => !isExpandedFormOpen.value && !isEditColumn.value :ref="focus" v-model="vModel" class="h-full w-full outline-none p-2 bg-transparent" - :placeholder="isEditColumn ? '(Optional)' : ''" + :placeholder="isEditColumn ? $t('labels.optional') : ''" + :class="{ + 'px-1': isExpandedFormOpen, + }" @blur="editEnabled = false" @keydown.down.stop @keydown.left.stop @@ -46,7 +49,7 @@ const focus: VNodeRef = (el) => !isExpandedFormOpen.value && !isEditColumn.value @mousedown.stop /> - NULL + {{ $t('general.null') }} diff --git a/packages/nc-gui/components/cell/TextArea.vue b/packages/nc-gui/components/cell/TextArea.vue index 0ebd53ae86..6cf430bd22 100644 --- a/packages/nc-gui/components/cell/TextArea.vue +++ b/packages/nc-gui/components/cell/TextArea.vue @@ -28,6 +28,8 @@ const isEditColumn = inject(EditColumnInj, ref(false)) const rowHeight = inject(RowHeightInj, ref(1 as const)) +const isForm = inject(IsFormInj, ref(false)) + const { showNull } = useGlobal() const vModel = useVModel(props, 'modelValue', emits, { defaultValue: '' }) @@ -68,10 +70,11 @@ onClickOutside(inputWrapperRef, (e) => {