diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 0a0596915f..02d321f178 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -61,7 +61,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-pg-restTableOps-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-pg-restViews-run-cache: @@ -106,7 +106,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-pg-restViews-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-pg-restRoles-run-cache: @@ -151,7 +151,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-pg-restRoles-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-pg-restMisc-run-cache: @@ -196,7 +196,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-pg-restMisc-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-restTableOps-run-cache: @@ -241,7 +241,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-restTableOps-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-restViews-run-cache: @@ -286,7 +286,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-restViews-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-restRoles-run-cache: @@ -331,7 +331,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-restRoles-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-restMisc-run-cache: @@ -376,7 +376,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-restMisc-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-xcdb-restTableOps-run-cache: @@ -421,7 +421,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-xcdb-restTableOps-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-xcdb-restViews-run-cache: @@ -466,7 +466,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-xcdb-restViews-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-xcdb-restRoles-run-cache: @@ -511,7 +511,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-xcdb-restRoles-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 cypress-xcdb-restMisc-run-cache: @@ -556,7 +556,7 @@ jobs: if: always() uses: actions/upload-artifact@v2 with: - name: restTableOps-snapshots + name: cypress-xcdb-restMisc-run-cache-snapshots path: scripts/cypress/screenshots retention-days: 2 # docker: diff --git a/.github/workflows/release-executables.yml b/.github/workflows/release-executables.yml new file mode 100644 index 0000000000..3857df89d6 --- /dev/null +++ b/.github/workflows/release-executables.yml @@ -0,0 +1,167 @@ +name: "Release : Executables" + +on: + # Triggered manually + workflow_dispatch: + inputs: + tag: + description: "Timely version" + required: true + # Triggered by release-nightly-dev.yml / release-pr.yml + workflow_call: + inputs: + tag: + description: "Timely version" + required: true + type: string + secrets: + NC_GITHUB_TOKEN: + required: true +jobs: + build-executables: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.NC_GITHUB_TOKEN }} + repository: 'nocodb/nocodb-timely' + - name: Cache node modules + id: cache-npm + uses: actions/cache@v3 + env: + cache-name: cache-node-modules + with: + # npm cache files are stored in `~/.npm` on Linux/macOS + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Cache pkg modules + id: cache-pkg + uses: actions/cache@v3 + env: + cache-name: cache-pkg + with: + # pkg cache files are stored in `~/.pkg-cache` + path: ~/.pkg-cache + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Install QEMU and ldid + run: | + # Install qemu + sudo apt install qemu binfmt-support qemu-user-static + # install ldid + git clone https://github.com/daeken/ldid.git + cd ./ldid + ./make.sh + sudo cp ./ldid /usr/local/bin + + - name: Update nocodb-timely + env: + TAG: ${{ github.event.inputs.tag || inputs.tag }} + run: | + npm i -E nocodb-daily@$TAG + + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + + git commit package.json -m "Update to $TAG" + git tag $TAG + git push --tags + + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name : Install dependencies and build executables + run: | + # install npm dependendencies + npm i + + # Copy sqlite binaries + rsync -rvzhP ./binaries/binding/ ./node_modules/sqlite3/lib/binding/ + + # clean up code to optimize size + npx modclean --patterns="default:*" --ignore="nc-lib-gui-daily/**,dayjs/**,express-status-monitor/**,sqlite3/**" --run + + # build executables + npm run build + + mkdir ./mac-dist + mv ./dist/Noco-macos-arm64 ./mac-dist/ + mv ./dist/Noco-macos-x64 ./mac-dist/ + + # Compress executables + GZIP=-9 tar -czvf ./dist/Noco-linux-x64.tar.gz ./dist/Noco-linux-x64 + GZIP=-9 tar -czvf ./dist/Noco-win-x64.tar.gz ./dist/Noco-win-x64.exe + GZIP=-9 tar -czvf ./dist/Noco-linux-arm64.tar.gz ./dist/Noco-linux-arm64 + GZIP=-9 tar -czvf ./dist/Noco-win-arm64.tar.gz ./dist/Noco-win-arm64.exe + + - name: Upload executables(except mac executables) to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.NC_GITHUB_TOKEN }} + file: dist/** + tag: ${{ github.event.inputs.tag || inputs.tag }} + overwrite: true + file_glob: true + repo_name: nocodb/nocodb-timely + + - uses: actions/upload-artifact@master + with: + name: ${{ github.event.inputs.tag || inputs.tag }} + path: mac-dist + retention-days: 1 + + sign-mac-executables: + runs-on: macos-latest + needs: build-executables + steps: + + - uses: actions/download-artifact@master + with: + name: ${{ github.event.inputs.tag || inputs.tag }} + path: mac-dist + + - name: Sign macOS executables + run: | + /usr/bin/codesign --force -s - ./mac-dist/Noco-macos-arm64 -v + /usr/bin/codesign --force -s - ./mac-dist/Noco-macos-x64 -v + + - uses: actions/upload-artifact@master + with: + name: ${{ github.event.inputs.tag || inputs.tag }} + path: mac-dist + retention-days: 1 + + + publish-mac-executables: + needs: sign-mac-executables + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@master + with: + name: ${{ github.event.inputs.tag || inputs.tag }} + path: mac-dist + - name: Compress files + run: | + GZIP=-9 tar -czvf ./mac-dist/Noco-macos-x64.tar.gz ./mac-dist/Noco-macos-x64 + GZIP=-9 tar -czvf ./mac-dist/Noco-macos-arm64.tar.gz ./mac-dist/Noco-macos-arm64 + + - name: Upload mac executables to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.NC_GITHUB_TOKEN }} + file: mac-dist/** + tag: ${{ github.event.inputs.tag || inputs.tag }} + overwrite: true + file_glob: true + repo_name: nocodb/nocodb-timely + + diff --git a/.github/workflows/release-nightly-dev.yml b/.github/workflows/release-nightly-dev.yml index af71aa58ff..76322d0135 100644 --- a/.github/workflows/release-nightly-dev.yml +++ b/.github/workflows/release-nightly-dev.yml @@ -37,7 +37,7 @@ jobs: outputs: nightly_build_tag: ${{ steps.tag-step.outputs.NIGHTLY_BUILD_TAG }} is_daily: ${{ steps.tag-step.outputs.IS_DAILY }} - current_version: ${{ steps.tag-step.outputs.CURRENT_VERSION }} + current_version: ${{ steps.tag-step.outputs.CURRENT_VERSION }} # Build frontend and backend and publish to npm release-npm: needs: set-tag @@ -48,6 +48,15 @@ jobs: secrets: NPM_TOKEN: "${{ secrets.NPM_TOKEN }}" + # Build executables and publish to GitHub + release-executables: + needs: [set-tag, release-npm] + uses: ./.github/workflows/release-executables.yml + with: + tag: ${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.nightly_build_tag }} + secrets: + NC_GITHUB_TOKEN: "${{ secrets.NC_GITHUB_TOKEN }}" + # Build docker image and push to docker hub release-docker: needs: [set-tag, release-npm] diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 38cce2c390..70aeed0bb0 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -50,7 +50,7 @@ jobs: echo ${{ steps.tag-step.outputs.CURRENT_VERSION }} outputs: target_tag: ${{ steps.tag-step.outputs.TARGET_TAG }} - current_version: ${{ steps.tag-step.outputs.CURRENT_VERSION }} + current_version: ${{ steps.tag-step.outputs.CURRENT_VERSION }} # Build, install, publish frontend and backend to npm release-npm: @@ -77,13 +77,22 @@ jobs: DOCKERHUB_USERNAME: "${{ secrets.DOCKERHUB_USERNAME }}" DOCKERHUB_TOKEN: "${{ secrets.DOCKERHUB_TOKEN }}" + # Build executables and publish to GitHub + release-executables: + needs: [set-tag, release-npm] + uses: ./.github/workflows/release-executables.yml + with: + tag: ${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }} + secrets: + NC_GITHUB_TOKEN: "${{ secrets.NC_GITHUB_TOKEN }}" + # leave-comment: # runs-on: 'ubuntu-latest' # needs: [release-docker, set-tag] # steps: # - run: | # echo docker run -d -p 8888:8080 nocodb/nocodb-timely:${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }} - + leave-comment: if: ${{ github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false }} runs-on: 'ubuntu-latest' @@ -96,7 +105,45 @@ jobs: ``` docker run -d -p 8888:8080 nocodb/nocodb-timely:${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }} ``` - + + leave-executable-comment: + if: ${{ github.actor != 'dependabot[bot]' && github.event.pull_request.draft == false }} + runs-on: 'ubuntu-latest' + needs: [release-executables, set-tag] + steps: + - uses: peter-evans/commit-comment@v2 + with: + body: | + ### Run Executables + + #### MacOS + + ```bash + mkdir -p ./${{ needs.set-tag.outputs.current_version }}/${{ needs.set-tag.outputs.target_tag }} \ + && cd ./${{ needs.set-tag.outputs.current_version }}/${{ needs.set-tag.outputs.target_tag }} \ + && curl http://dl.nocodb.com/${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }}/Noco-macos-arm64 -o noco -L \ + && chmod +x noco \ + && ./noco + ``` + #### Linux + + ```bash + mkdir -p ./${{ needs.set-tag.outputs.current_version }}/${{ needs.set-tag.outputs.target_tag }} \ + && cd ./${{ needs.set-tag.outputs.current_version }}/${{ needs.set-tag.outputs.target_tag }} \ + && curl http://dl.nocodb.com/${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }}/Noco-linux-x64 -o noco -L \ + && chmod +x noco \ + && ./noco + ``` + #### windows + + ```bash + iwp http://dl.nocodb.com/${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }}/Noco-win-arm64.exe + .\Noco-win-arm64.exe + ``` + + For executables visit [here](https://github.com/nocodb/nocodb-timely/releases/tag/${{ needs.set-tag.outputs.current_version }}-${{ needs.set-tag.outputs.target_tag }}) + + # if-merged: # if: github.event.pull_request.merged == true # runs-on: ubuntu-latest diff --git a/package.json b/package.json index 559c8d471b..579c3db173 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "start:api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress", "start:xcdb-api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress", "start:api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress", + "start:api:cache:pg": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress:pg", "start:xcdb-api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress", "start:web": "cd ./packages/nc-gui; npm install; npm run dev", "cypress:run": "cypress run --config-file ./scripts/cypress/cypress.json", diff --git a/packages/nc-gui/components/CreateOrEditProject.vue b/packages/nc-gui/components/CreateOrEditProject.vue index 831b4fa489..fe943c6354 100644 --- a/packages/nc-gui/components/CreateOrEditProject.vue +++ b/packages/nc-gui/components/CreateOrEditProject.vue @@ -89,7 +89,7 @@ ref="name" v-model="project.title" v-ge="['project', 'name']" - :rules="form.titleRequiredRule" + :rules="form.titleValidationRule" :height="20" :label="$t('placeholder.projName')" autofocus @@ -963,7 +963,10 @@ export default { /** ************** START : form related ****************/ form: { portValidationRule: [v => /^\d+$/.test(v) || 'Not a valid port'], - titleRequiredRule: [v => !!v || 'Title is required'], + titleValidationRule: [ + v => !!v || 'Title is required', + v => v.length <= 50 || 'Project name exceeds 50 characters', + ], requiredRule: [v => !!v || 'Field is required'], folderRequiredRule: [v => !!v || 'Folder path is required'] }, diff --git a/packages/nc-gui/components/import/QuickImport.vue b/packages/nc-gui/components/import/QuickImport.vue index 3ac14f1a68..49842bdf59 100644 --- a/packages/nc-gui/components/import/QuickImport.vue +++ b/packages/nc-gui/components/import/QuickImport.vue @@ -60,11 +60,18 @@ v-model="url" hide-details="auto" type="url" - :label="quickImportType == 'excel' ? $t('msg.info.excelURL') : $t('msg.info.csvURL') " + :label="quickImportType === 'excel' ? $t('msg.info.excelURL') : $t('msg.info.csvURL') " class="caption" outlined dense - :rules="[v => !!v || $t('general.required') ]" + :rules=" + [ + v => !!v || $t('general.required'), + v => !(/(10)(\.([2]([0-5][0-5]|[01234][6-9])|[1][0-9][0-9]|[1-9][0-9]|[0-9])){3}|(172)\.(1[6-9]|2[0-9]|3[0-1])(\.(2[0-4][0-9]|25[0-5]|[1][0-9][0-9]|[1-9][0-9]|[0-9])){2}|(192)\.(168)(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){2}|(0.0.0.0)|localhost?/g).test(v) || errorMessages.ipBlockList, + v => quickImportType === 'excel' ? + (/.*\.(xls|xlsx|xlsm|ods|ots)/.test(v) || errorMessages.importExcel) : + (/.*\.(csv)/.test(v) || errorMessages.importCSV), + ]" /> @@ -213,7 +220,12 @@ export default { parserConfig: { maxRowsToParse: 500 }, - filename: '' + filename: '', + errorMessages: { + importExcel: "Target file is not an accepted file type. The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots!", + importCSV: "Target file is not an accepted file type. The accepted file type is .csv!", + ipBlockList: "IP Not allowed!" + } } }, computed: { @@ -288,7 +300,7 @@ export default { templateGenerator = new ExcelTemplateAdapter(name, val, this.parserConfig) break case 'url': - templateGenerator = new ExcelUrlTemplateAdapter(val, this.$store, this.parserConfig, this.$api) + templateGenerator = new ExcelUrlTemplateAdapter(val, this.$store, this.parserConfig, this.$api, this.quickImportType) break } await templateGenerator.init() @@ -322,11 +334,11 @@ export default { if (this.quickImportType === 'excel') { if (!/.*\.(xls|xlsx|xlsm|ods|ots)/.test(file.name)) { - return this.$toast.error('Dropped file is not an accepted file type. The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots!').goAway(3000) + return this.$toast.error(this.errorMessages.importExcel).goAway(3000) } } else if (this.quickImportType === 'csv') { if (!/.*\.(csv)/.test(file.name)) { - return this.$toast.error('Dropped file is not an accepted file type. The accepted file type is .csv!').goAway(3000) + return this.$toast.error(this.errorMessages.importCSV).goAway(3000) } } this._file(file) diff --git a/packages/nc-gui/components/import/templateParsers/ExcelTemplateAdapter.js b/packages/nc-gui/components/import/templateParsers/ExcelTemplateAdapter.js index 9202e4ab31..1bd0107d15 100644 --- a/packages/nc-gui/components/import/templateParsers/ExcelTemplateAdapter.js +++ b/packages/nc-gui/components/import/templateParsers/ExcelTemplateAdapter.js @@ -27,7 +27,25 @@ export default class ExcelTemplateAdapter extends TemplateGenerator { } async init() { - this.wb = XLSX.read(new Uint8Array(this.excelData), { type: 'array', cellText: true, cellDates: true }) + const options = { + cellText: true, + cellDates: true + } + if (this.name.slice(-3) === 'csv') { + this.wb = XLSX.read( + (new TextDecoder).decode(new Uint8Array(this.excelData)), + { + type: "string", + ...options + }); + } else { + this.wb = XLSX.read( + new Uint8Array(this.excelData), + { + type: 'array', + ...options + }) + } } parse() { diff --git a/packages/nc-gui/components/import/templateParsers/ExcelUrlTemplateAdapter.js b/packages/nc-gui/components/import/templateParsers/ExcelUrlTemplateAdapter.js index a263ccc0fd..d8a91ac3d2 100644 --- a/packages/nc-gui/components/import/templateParsers/ExcelUrlTemplateAdapter.js +++ b/packages/nc-gui/components/import/templateParsers/ExcelUrlTemplateAdapter.js @@ -1,19 +1,19 @@ import ExcelTemplateAdapter from '~/components/import/templateParsers/ExcelTemplateAdapter' export default class ExcelUrlTemplateAdapter extends ExcelTemplateAdapter { - constructor(url, $store, parserConfig, $api) { + constructor(url, $store, parserConfig, $api, quickImportType) { const name = url.split('/').pop() super(name, null, parserConfig) this.url = url this.$api = $api this.$store = $store + this.quickImportType = quickImportType } async init() { const data = await this.$api.utils.axiosRequestMake({ apiMeta: { - url: this.url, - responseType: 'arraybuffer' + url: this.url } }) this.excelData = data.data diff --git a/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue b/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue index a2cebccb06..d0df405eed 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/editColumn/FormulaOptions.vue @@ -267,10 +267,7 @@ export default { // validate data type if (parsedTree.callee.type === jsep.IDENTIFIER) { const expectedType = formulas[parsedTree.callee.name].type - if ( - expectedType === formulaTypes.NUMERIC || - expectedType === formulaTypes.STRING - ) { + if (expectedType === formulaTypes.NUMERIC) { parsedTree.arguments.map(arg => this.validateAgainstType(arg, expectedType, null, typeErrors)) } else if (expectedType === formulaTypes.DATE) { if (parsedTree.callee.name === 'DATEADD') { diff --git a/packages/nc-gui/components/project/spreadsheet/views/FormView.vue b/packages/nc-gui/components/project/spreadsheet/views/FormView.vue index e3c8639b56..fe0beb255a 100644 --- a/packages/nc-gui/components/project/spreadsheet/views/FormView.vue +++ b/packages/nc-gui/components/project/spreadsheet/views/FormView.vue @@ -617,35 +617,21 @@ export default { ) { continue } - if ( - !column.virtual && - (((column.rqd || column.notnull) && !column.cdf) || - (column.pk && !(column.ai || column.default)) || - this.localParams.fields[column.title].required) - ) { + if (!isVirtualCol(column) && (((column.rqd || column.notnull) && !column.cdf) || (column.pk && !(column.ai || column.cdf)) || column.required)) { obj.localState[column.title] = { required } - } else if (column.bt) { - const col = this.meta.columns.find( - c => c.column_name === column.bt.column_name - ) - if ( - (col.rqd && !col.default) || - this.localParams.fields[column.title].required - ) { - obj.localState[col.title] = { required } + } else if (column.uidt === UITypes.LinkToAnotherRecord && column.colOptions && column.colOptions.type === RelationTypes.BELONGS_TO) { + const col = this.meta.columns.find(c => c.id === column.colOptions.fk_child_column_id) + + if ((col && col.rqd && !col.cdf) || column.required) { + if (col) { obj.virtual[column.title] = { required } } } - } else if ( - column.virtual && - this.localParams.fields[column.title].required && - (column.mm || column.hm) - ) { + } else if (isVirtualCol(column) && column.required) { obj.virtual[column.title] = { minLength: minLength(1), required } } } - return obj }, computed: { diff --git a/packages/nc-gui/pages/project/id.vue b/packages/nc-gui/pages/project/id.vue index 46069ba5bf..1a70589d7b 100644 --- a/packages/nc-gui/pages/project/id.vue +++ b/packages/nc-gui/pages/project/id.vue @@ -84,7 +84,7 @@ /^\d+$/.test(v) || 'Not a valid port'], - titleRequiredRule: [v => !!v || 'Title is required'], + titleValidationRule: [ + v => !!v || 'Title is required', + v => v.length <= 50 || 'Project name exceeds 50 characters', + ], requiredRule: [v => !!v || 'Field is required'], folderRequiredRule: [v => !!v || 'Folder path is required'] }, diff --git a/packages/nc-gui/pages/project/xcdb.vue b/packages/nc-gui/pages/project/xcdb.vue index 18534c3a86..8c1c32aeb3 100644 --- a/packages/nc-gui/pages/project/xcdb.vue +++ b/packages/nc-gui/pages/project/xcdb.vue @@ -39,7 +39,7 @@ :full-width="false" class="nc-metadb-project-name" :label="$t('placeholder.projName')" - :rules="[v => !!v || $t('general.required')]" + :rules="form.titleValidationRule" />