From 4bba6398fa2aa9fcfe0ceac3c3f7d8e4329cf029 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 21 Sep 2022 22:03:46 +0530 Subject: [PATCH 001/251] feat: filter improvement - avoid unnecessary data reload and meta reload - add debounce to input field to apply filter only after a delay rather thatn on each key press Signed-off-by: Pranav C --- .../smartsheet-toolbar/ColumnFilter.vue | 19 ++++++++++--------- packages/nc-gui/composables/useViewFilters.ts | 10 ++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue index db3fb430ed..eb76546c0e 100644 --- a/packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue +++ b/packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue @@ -44,14 +44,15 @@ const reloadDataHook = inject(ReloadViewDataHookInj)! const { $e } = useNuxtApp() const { nestedFilters } = useSmartsheetStoreOrThrow() -const { filters, deleteFilter, saveOrUpdate, loadFilters, addFilter, addFilterGroup, sync } = useViewFilters( - activeView, - parentId, - computed(() => autoSave), - () => reloadDataHook.trigger(showLoading), - modelValue || nestedFilters.value, - !modelValue, -) +const { filters, deleteFilter, saveOrUpdate, loadFilters, addFilter, addFilterGroup, sync, saveOrUpdateDebounced } = + useViewFilters( + activeView, + parentId, + computed(() => autoSave), + () => reloadDataHook.trigger(showLoading), + modelValue || nestedFilters.value, + !modelValue, + ) const localNestedFilters = ref() @@ -265,7 +266,7 @@ defineExpose({ class="nc-filter-value-select" :disabled="filter.readOnly" @click.stop - @input="saveOrUpdate(filter, i)" + @input="saveOrUpdateDebounced(filter, i)" /> diff --git a/packages/nc-gui/composables/useViewFilters.ts b/packages/nc-gui/composables/useViewFilters.ts index 331162e4c2..95d11f4706 100644 --- a/packages/nc-gui/composables/useViewFilters.ts +++ b/packages/nc-gui/composables/useViewFilters.ts @@ -3,11 +3,11 @@ import type { ComputedRef, Ref } from 'vue' import { message } from 'ant-design-vue' import { IsPublicInj, - ReloadViewDataHookInj, computed, extractSdkResponseErrorMsg, inject, ref, + useDebounceFn, useMetas, useNuxtApp, useUIPermission, @@ -23,8 +23,6 @@ export function useViewFilters( currentFilters?: Filter[], isNestedRoot?: boolean, ) { - const reloadHook = inject(ReloadViewDataHookInj) - const { nestedFilters } = useSmartsheetStoreOrThrow() const isPublic = inject(IsPublicInj, ref(false)) @@ -173,8 +171,6 @@ export function useViewFilters( fk_parent_id: parentId, }) } - - reloadHook?.trigger() } catch (e: any) { console.log(e) message.error(await extractSdkResponseErrorMsg(e)) @@ -183,6 +179,8 @@ export function useViewFilters( reloadData?.() } + const saveOrUpdateDebounced = useDebounceFn(saveOrUpdate, 500) + const addFilter = () => { filters.value.push({ ...placeholderFilter }) $e('a:filter:add', { length: filters.value.length }) @@ -221,5 +219,5 @@ export function useViewFilters( }, ) - return { filters, loadFilters, sync, deleteFilter, saveOrUpdate, addFilter, addFilterGroup } + return { filters, loadFilters, sync, deleteFilter, saveOrUpdate, addFilter, addFilterGroup, saveOrUpdateDebounced } } From 2287ae658128cbf529456f8aad669a0e03ee79cf Mon Sep 17 00:00:00 2001 From: Ekaterina Balakina Date: Fri, 18 Nov 2022 02:33:39 +0100 Subject: [PATCH 002/251] use cell inputs for filters input type --- .../smartsheet/toolbar/ColumnFilter.vue | 25 ++- .../smartsheet/toolbar/FilterInput.vue | 159 ++++++++++++++++++ .../pages/Dashboard/WebhookForm/index.ts | 2 +- .../pages/Dashboard/common/Toolbar/Filter.ts | 4 +- 4 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue diff --git a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue index 9bb1fedc8e..4b4c3eb48f 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue @@ -100,6 +100,15 @@ watch( }, ) +const getColumn = (filter: Filter) => { + return columns.value?.find((col) => col.id === filter.fk_column_id) +} + +const selectFilterField = (filter: Filter, index: number) => { + filter.value = null + saveOrUpdate(filter, index) +} + const applyChanges = async (hookId?: string, _nested = false) => { await sync(hookId, _nested) @@ -198,7 +207,7 @@ defineExpose({ :columns="columns" :disabled="filter.readOnly" @click.stop - @change="saveOrUpdate(filter, i)" + @change="selectFilterField(filter, i)" /> - diff --git a/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue b/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue new file mode 100644 index 0000000000..2d1308dc40 --- /dev/null +++ b/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue @@ -0,0 +1,159 @@ + + + diff --git a/tests/playwright/pages/Dashboard/WebhookForm/index.ts b/tests/playwright/pages/Dashboard/WebhookForm/index.ts index 7eff8ebcea..6fdc6e0602 100644 --- a/tests/playwright/pages/Dashboard/WebhookForm/index.ts +++ b/tests/playwright/pages/Dashboard/WebhookForm/index.ts @@ -87,7 +87,7 @@ export class WebhookFormPage extends BasePage { await this.rootPage.waitForTimeout(1500); if (operator != 'is null' && operator != 'is not null') { - await modal.locator('input.nc-filter-value-select').fill(value); + await modal.locator('.nc-filter-value-select > input').fill(value); } if (save) { diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts b/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts index 6c7a8b70c4..f0a536a4d2 100644 --- a/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts +++ b/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts @@ -18,7 +18,7 @@ export class ToolbarFilterPage extends BasePage { await expect(this.get().locator('.nc-filter-field-select').nth(index)).toHaveText(column); await expect(this.get().locator('.nc-filter-operation-select').nth(index)).toHaveText(operator); await expect - .poll(async () => await this.get().locator('input.nc-filter-value-select').nth(index).inputValue()) + .poll(async () => await this.get().locator('.nc-filter-value-select > input').nth(index).inputValue()) .toBe(value); } @@ -70,7 +70,7 @@ export class ToolbarFilterPage extends BasePage { await this.toolbar.parent.dashboard.waitForLoaderToDisappear(); } - const fillFilter = this.rootPage.locator('.nc-filter-value-select').last().fill(value); + const fillFilter = this.rootPage.locator('.nc-filter-value-select > input').last().fill(value); await this.waitForResponse({ uiAction: fillFilter, httpMethodsToMatch: ['GET'], From b419bf0f2699088fcdf5a7bf14f22272c964333b Mon Sep 17 00:00:00 2001 From: Ekaterina Balakina Date: Sat, 26 Nov 2022 13:09:07 +0100 Subject: [PATCH 003/251] review fixes --- packages/nc-gui/components/cell/Duration.vue | 5 +- .../nc-gui/components/cell/MultiSelect.vue | 9 +- .../nc-gui/components/cell/SingleSelect.vue | 18 +++- .../smartsheet/toolbar/FilterInput.vue | 85 ++++++++++++------- 4 files changed, 82 insertions(+), 35 deletions(-) diff --git a/packages/nc-gui/components/cell/Duration.vue b/packages/nc-gui/components/cell/Duration.vue index bcb32c34fc..a6bf5b047d 100644 --- a/packages/nc-gui/components/cell/Duration.vue +++ b/packages/nc-gui/components/cell/Duration.vue @@ -13,9 +13,10 @@ import { interface Props { modelValue: number | string | null | undefined + showValidationError: boolean } -const { modelValue } = defineProps() +const { modelValue, showValidationError = true } = defineProps() const emit = defineEmits(['update:modelValue']) @@ -93,7 +94,7 @@ const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus() {{ localState }} -
+
Please enter a number
diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 406c869dda..7269b5691c 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -27,9 +27,10 @@ import MdiCloseCircle from '~icons/mdi/close-circle' interface Props { modelValue?: string | string[] rowIndex?: number + disableOptionCreation?: boolean } -const { modelValue } = defineProps() +const { modelValue, disableOptionCreation } = defineProps() const emit = defineEmits(['update:modelValue']) @@ -303,7 +304,11 @@ const onTagClick = (e: Event, onClose: Function) => { - +
diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index 0445193150..62e89fae6e 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -14,6 +14,7 @@ import { extractSdkResponseErrorMsg, inject, ref, + useEventListener, useSelectedCellKeyupListener, watch, } from '#imports' @@ -21,9 +22,10 @@ import { interface Props { modelValue?: string | undefined rowIndex?: number + disableOptionCreation?: boolean } -const { modelValue } = defineProps() +const { modelValue, disableOptionCreation } = defineProps() const emit = defineEmits(['update:modelValue']) @@ -176,6 +178,14 @@ const toggleMenu = (e: Event) => { } isOpen.value = (active.value || editable.value) && !isOpen.value } + +const handleClose = (e: MouseEvent) => { + if (aselect.value && !aselect.value.$el.contains(e.target)) { + isOpen.value = false + } +} + +useEventListener(document, 'click', handleClose) From 241f925e0a65d191cd7bd84f2619d828073f54ad Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Mon, 28 Nov 2022 12:09:08 +0530 Subject: [PATCH 004/251] chore(test): Removed clicking on the single select cell, after clearing in single select, since click on clear closes the dropdown --- tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts b/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts index 82953a7124..bb7389c033 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts @@ -65,7 +65,6 @@ export class SelectOptionCellPageObject extends BasePage { await this.get({ index, columnHeader }).click(); await this.rootPage.locator('.ant-select-single > .ant-select-clear').click(); - await this.cell.get({ index, columnHeader }).click(); await this.rootPage.locator(`.nc-dropdown-single-select-cell`).waitFor({ state: 'hidden' }); } From fe94f1adcba93f5fff171badcdfe80e6a22ba8d9 Mon Sep 17 00:00:00 2001 From: Ekaterina Balakina Date: Mon, 28 Nov 2022 16:44:24 +0100 Subject: [PATCH 005/251] fix abstract type --- packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue b/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue index 13fad5a871..f12b28fed8 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue @@ -81,7 +81,7 @@ type FilterType = keyof typeof checkTypeFunctions const { sqlUi } = $(useProject()) -const abstractType = $computed(() => column.value && sqlUi?.getAbstractType(column.value)) +const abstractType = $computed(() => (column.value?.dt && sqlUi ? sqlUi.getAbstractType(column.value) : null)) const checkType = (filterType: FilterType) => { const checkTypeFunction = checkTypeFunctions[filterType] From 6633362f24e0ef47c393955d91b0039a3091918c Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Mon, 30 Jan 2023 13:29:22 +0800 Subject: [PATCH 006/251] chore(nc-gui): lint --- packages/nc-gui/components/cell/MultiSelect.vue | 8 +++++++- packages/nc-gui/components/cell/SingleSelect.vue | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 810e621c09..6fce902953 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -318,7 +318,13 @@ const onTagClick = (e: Event, onClose: Function) => { diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index f652f9d7d3..ed206e8eef 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -241,7 +241,13 @@ useEventListener(document, 'click', handleClose) From 087ef2e7acc9bc62a2744cbfbfee3c5ed151d6ce Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Mon, 30 Jan 2023 13:44:37 +0800 Subject: [PATCH 007/251] chore(nc-gui): hyphenate v-on event --- packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue index 73d1240f82..a00e68686c 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue @@ -256,7 +256,7 @@ defineExpose({ class="nc-filter-value-select" :column="getColumn(filter)" :filter="filter" - @updateFilterValue=" + @update-filter-value=" (value) => { filter.value = value saveOrUpdate(filter, i) From e87c0fdbd51ecf7d19b6019f5f65b164b4fc18b4 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Mon, 30 Jan 2023 14:02:44 +0800 Subject: [PATCH 008/251] chore(nc-gui): lint --- .../smartsheet/toolbar/ColumnFilter.vue | 12 +++++++++++- packages/nc-gui/composables/useViewFilters.ts | 12 +++++++++++- packages/nc-gui/package-lock.json | 19 +++++++++++++++++++ packages/nc-gui/package.json | 1 + 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue index 6298f91907..31241cecb1 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue @@ -42,7 +42,17 @@ const reloadDataHook = inject(ReloadViewDataHookInj)! const { $e } = useNuxtApp() const { nestedFilters } = useSmartsheetStoreOrThrow() -const { filters, nonDeletedFilters, deleteFilter, saveOrUpdate, loadFilters, addFilter, addFilterGroup, sync, saveOrUpdateDebounced } = useViewFilters( +const { + filters, + nonDeletedFilters, + deleteFilter, + saveOrUpdate, + loadFilters, + addFilter, + addFilterGroup, + sync, + saveOrUpdateDebounced, +} = useViewFilters( activeView, parentId, computed(() => autoSave), diff --git a/packages/nc-gui/composables/useViewFilters.ts b/packages/nc-gui/composables/useViewFilters.ts index b372dca902..98f03f6631 100644 --- a/packages/nc-gui/composables/useViewFilters.ts +++ b/packages/nc-gui/composables/useViewFilters.ts @@ -239,5 +239,15 @@ export function useViewFilters( }, ) - return { filters, nonDeletedFilters, loadFilters, sync, deleteFilter, saveOrUpdate, addFilter, addFilterGroup, saveOrUpdateDebounced } + return { + filters, + nonDeletedFilters, + loadFilters, + sync, + deleteFilter, + saveOrUpdate, + addFilter, + addFilterGroup, + saveOrUpdateDebounced, + } } diff --git a/packages/nc-gui/package-lock.json b/packages/nc-gui/package-lock.json index 09a4b55412..bb6950233c 100644 --- a/packages/nc-gui/package-lock.json +++ b/packages/nc-gui/package-lock.json @@ -54,6 +54,7 @@ "@iconify-json/clarity": "^1.1.4", "@iconify-json/eva": "^1.1.2", "@iconify-json/ic": "^1.1.7", + "@iconify-json/la": "^1.1.2", "@iconify-json/logos": "^1.1.14", "@iconify-json/lucide": "^1.1.36", "@iconify-json/material-symbols": "^1.1.8", @@ -1119,6 +1120,15 @@ "@iconify/types": "*" } }, + "node_modules/@iconify-json/la": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@iconify-json/la/-/la-1.1.2.tgz", + "integrity": "sha512-Cv93a5X5n9gYeUeQ7h9z5tmoZzChvwvbCorBQwMQgwCnMQynH6dCKdtbtYsZyT5wH4QYwywv7xgvpBIkqvZgqg==", + "dev": true, + "dependencies": { + "@iconify/types": "*" + } + }, "node_modules/@iconify-json/logos": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.1.18.tgz", @@ -18589,6 +18599,15 @@ "@iconify/types": "*" } }, + "@iconify-json/la": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@iconify-json/la/-/la-1.1.2.tgz", + "integrity": "sha512-Cv93a5X5n9gYeUeQ7h9z5tmoZzChvwvbCorBQwMQgwCnMQynH6dCKdtbtYsZyT5wH4QYwywv7xgvpBIkqvZgqg==", + "dev": true, + "requires": { + "@iconify/types": "*" + } + }, "@iconify-json/logos": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.1.18.tgz", diff --git a/packages/nc-gui/package.json b/packages/nc-gui/package.json index b7a3e9e7db..b37773df3f 100644 --- a/packages/nc-gui/package.json +++ b/packages/nc-gui/package.json @@ -77,6 +77,7 @@ "@iconify-json/clarity": "^1.1.4", "@iconify-json/eva": "^1.1.2", "@iconify-json/ic": "^1.1.7", + "@iconify-json/la": "^1.1.2", "@iconify-json/logos": "^1.1.14", "@iconify-json/lucide": "^1.1.36", "@iconify-json/material-symbols": "^1.1.8", From 646e4355af5bd2f2d5b443828d1ad3f2ccc7e288 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Tue, 31 Jan 2023 18:24:52 +0800 Subject: [PATCH 009/251] feat(nc-gui): add placeholder to filter value input --- packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue b/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue index f12b28fed8..cf55b3096c 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/FilterInput.vue @@ -178,6 +178,7 @@ const hasExtraPadding = $computed(() => { :is="filterType ? componentMap[filterType] : Text" v-model="filterInput" :disabled="filter.readOnly" + placeholder="Enter a value" :column="column" class="flex" v-bind="componentProps" From 625c59e2f54ab4b0b33fb81a7f4d1d402be4cb02 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Wed, 1 Feb 2023 18:07:20 +0800 Subject: [PATCH 010/251] fix(nocodb): condition nlike logic --- .../db/sql-data-mapper/lib/sql/conditionV2.ts | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts index 54ad7faaea..5c96d1a36a 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts @@ -297,20 +297,34 @@ const parseConditionV2 = async ( } break; case 'nlike': - if (column.uidt === UITypes.Formula) { - [field, val] = [val, field]; - val = `%${val}%`.replace(/^%'([\s\S]*)'%$/, '%$1%'); + if (!val) { + // val is empty -> include all values but empty strings + qb.whereNot(field, ''); + qb.orWhereNull(field); } else { - val = val.startsWith('%') || val.endsWith('%') ? val : `%${val}%`; - } - qb.where((nestedQb) => { - if (qb?.client?.config?.client === 'pg') { - nestedQb.whereRaw('??::text not ilike ?', [field, val]); + if (column.uidt === UITypes.Formula) { + [field, val] = [val, field]; + val = `%${val}%`.replace(/^%'([\s\S]*)'%$/, '%$1%'); } else { - nestedQb.whereNot(field, 'like', val); + val = + val.startsWith('%') || val.endsWith('%') ? val : `%${val}%`; } - nestedQb.orWhereNull(field); - }); + qb.where((nestedQb) => { + if (qb?.client?.config?.client === 'pg') { + nestedQb.whereRaw('??::text not ilike ?', [field, val]); + } else { + nestedQb.whereNot(field, 'like', val); + } + if (val !== '%%') { + // if value is not empty, empty or null should be included + nestedQb.orWhere(field, ''); + nestedQb.orWhereNull(field); + } else { + // if value is empty, then only null is included + nestedQb.orWhereNull(field); + } + }); + } break; case 'allof': case 'anyof': From 0cf9f4d4e4e6c27f3bec05d31c67756bf38a7111 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Wed, 1 Feb 2023 18:13:04 +0800 Subject: [PATCH 011/251] fix(nocodb): condition like logic --- .../db/sql-data-mapper/lib/sql/conditionV2.ts | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts index 5c96d1a36a..9281e4ceb1 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts @@ -284,21 +284,28 @@ const parseConditionV2 = async ( }); break; case 'like': - if (column.uidt === UITypes.Formula) { - [field, val] = [val, field]; - val = `%${val}%`.replace(/^%'([\s\S]*)'%$/, '%$1%'); - } else { - val = val.startsWith('%') || val.endsWith('%') ? val : `%${val}%`; - } - if (qb?.client?.config?.client === 'pg') { - qb = qb.whereRaw('??::text ilike ?', [field, val]); + if (!val) { + // val is empty -> all values including empty strings but NULL + qb.where(field, ''); + qb.orWhereNotNull(field); } else { - qb = qb.where(field, 'like', val); + if (column.uidt === UITypes.Formula) { + [field, val] = [val, field]; + val = `%${val}%`.replace(/^%'([\s\S]*)'%$/, '%$1%'); + } else { + val = + val.startsWith('%') || val.endsWith('%') ? val : `%${val}%`; + } + if (qb?.client?.config?.client === 'pg') { + qb = qb.whereRaw('??::text ilike ?', [field, val]); + } else { + qb = qb.where(field, 'like', val); + } } break; case 'nlike': if (!val) { - // val is empty -> include all values but empty strings + // val is empty -> all values including NULL but empty strings qb.whereNot(field, ''); qb.orWhereNull(field); } else { From e17152cb4ba340a80300ac6980444181ce6bee6d Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Wed, 1 Feb 2023 18:57:30 +0800 Subject: [PATCH 012/251] fix(nc-gui): show empty string instead of the previous cell result --- packages/nc-gui/components/cell/ClampedText.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nc-gui/components/cell/ClampedText.vue b/packages/nc-gui/components/cell/ClampedText.vue index 56f85bd5e8..ad3e0c76ac 100644 --- a/packages/nc-gui/components/cell/ClampedText.vue +++ b/packages/nc-gui/components/cell/ClampedText.vue @@ -19,6 +19,6 @@ onMounted(() => { From 2e87dbeaf95d0efaea3f1f0d59471917cf79f4f2 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Wed, 1 Feb 2023 19:31:54 +0800 Subject: [PATCH 013/251] fix(nocodb): condition notempty logic --- .../nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts index 9281e4ceb1..dca95c9a0e 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts @@ -396,7 +396,9 @@ const parseConditionV2 = async ( else if (filter.value === 'empty') qb = qb.where(customWhereClause || field, ''); else if (filter.value === 'notempty') - qb = qb.whereNot(customWhereClause || field, ''); + qb = qb + .whereNot(customWhereClause || field, '') + .orWhereNull(field); else if (filter.value === 'true') qb = qb.where(customWhereClause || field, true); else if (filter.value === 'false') From a0ccee055d383a008d79755ea3d87d65a2a89629 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 11:30:13 +0800 Subject: [PATCH 014/251] feat(nc-gui): only include numeric ui types for >, <, >= and <= --- packages/nc-gui/utils/filterUtils.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index 53822a76d0..e9a14d7274 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -1,5 +1,7 @@ import { UITypes } from 'nocodb-sdk' +const numericUITypes = [UITypes.Duration, UITypes.Currency, UITypes.Percent, UITypes.Number, UITypes.Rating, UITypes.Rollup] + export const comparisonOpList: { text: string value: string @@ -82,21 +84,21 @@ export const comparisonOpList: { { text: '>', value: 'gt', - excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect, UITypes.SingleSelect], + includedTypes: [...numericUITypes], }, { text: '<', value: 'lt', - excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect, UITypes.SingleSelect], + includedTypes: [...numericUITypes], }, { text: '>=', value: 'gte', - excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect, UITypes.SingleSelect], + includedTypes: [...numericUITypes], }, { text: '<=', value: 'lte', - excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect, UITypes.SingleSelect], + includedTypes: [...numericUITypes], }, ] From 441950d0f51eebbcd002bb85b6c601aa2a31b5e4 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 11:38:07 +0800 Subject: [PATCH 015/251] feat(nc-gui): exclude numeric UI Types for like & empty operators --- packages/nc-gui/utils/filterUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index e9a14d7274..a72f102e65 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -32,24 +32,24 @@ export const comparisonOpList: { { text: 'is like', value: 'like', - excludedTypes: [UITypes.Checkbox], + excludedTypes: [UITypes.Checkbox, ...numericUITypes], }, { text: 'is not like', value: 'nlike', - excludedTypes: [UITypes.Checkbox], + excludedTypes: [UITypes.Checkbox, ...numericUITypes], }, { text: 'is empty', value: 'empty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, UITypes.Rating, UITypes.Number, UITypes.Decimal, UITypes.Percent, UITypes.Currency], + excludedTypes: [UITypes.Checkbox, ...numericUITypes], }, { text: 'is not empty', value: 'notempty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, UITypes.Rating, UITypes.Number, UITypes.Decimal, UITypes.Percent, UITypes.Currency], + excludedTypes: [UITypes.Checkbox, ...numericUITypes], }, { text: 'is null', From d0bb200dd69a58505f784b8e497d5ac13468b375 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 11:42:32 +0800 Subject: [PATCH 016/251] feat(nc-gui): exclude single select & collaborator for like & empty --- packages/nc-gui/utils/filterUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index a72f102e65..5e6d737539 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -32,24 +32,24 @@ export const comparisonOpList: { { text: 'is like', value: 'like', - excludedTypes: [UITypes.Checkbox, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is not like', value: 'nlike', - excludedTypes: [UITypes.Checkbox, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is empty', value: 'empty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is not empty', value: 'notempty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is null', From 5f4202a26b30f9052d7e6fc59f20c9d20d73f7ea Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 11:44:52 +0800 Subject: [PATCH 017/251] feat(nc-gui): exclude equal for checkbox --- packages/nc-gui/utils/filterUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index 5e6d737539..a5e075e716 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -24,10 +24,12 @@ export const comparisonOpList: { { text: 'is equal', value: 'eq', + excludedTypes: [UITypes.Checkbox], }, { text: 'is not equal', value: 'neq', + excludedTypes: [UITypes.Checkbox], }, { text: 'is like', From 1b2150a4493cd2d981b5dec00a3a7dc8eafbb103 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 11:58:55 +0800 Subject: [PATCH 018/251] feat(nc-gui): revise filter operators for multiselect --- packages/nc-gui/utils/filterUtils.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index a5e075e716..512058963b 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -24,34 +24,34 @@ export const comparisonOpList: { { text: 'is equal', value: 'eq', - excludedTypes: [UITypes.Checkbox], + excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect], }, { text: 'is not equal', value: 'neq', - excludedTypes: [UITypes.Checkbox], + excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect], }, { text: 'is like', value: 'like', - excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.MultiSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is not like', value: 'nlike', - excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.MultiSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is empty', value: 'empty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.MultiSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is not empty', value: 'notempty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.Collaborator, ...numericUITypes], + excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.MultiSelect, UITypes.Collaborator, ...numericUITypes], }, { text: 'is null', From 0e7e55ea92f1737136fdba154a2c42af5a4724dd Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 12:17:16 +0800 Subject: [PATCH 019/251] feat(nc-gui): exclude operators for attachment --- packages/nc-gui/utils/filterUtils.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index 512058963b..df42fb033e 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -24,12 +24,12 @@ export const comparisonOpList: { { text: 'is equal', value: 'eq', - excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect], + excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect, UITypes.Attachment], }, { text: 'is not equal', value: 'neq', - excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect], + excludedTypes: [UITypes.Checkbox, UITypes.MultiSelect, UITypes.Attachment], }, { text: 'is like', @@ -45,13 +45,27 @@ export const comparisonOpList: { text: 'is empty', value: 'empty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.MultiSelect, UITypes.Collaborator, ...numericUITypes], + excludedTypes: [ + UITypes.Checkbox, + UITypes.SingleSelect, + UITypes.MultiSelect, + UITypes.Collaborator, + UITypes.Attachment, + ...numericUITypes, + ], }, { text: 'is not empty', value: 'notempty', ignoreVal: true, - excludedTypes: [UITypes.Checkbox, UITypes.SingleSelect, UITypes.MultiSelect, UITypes.Collaborator, ...numericUITypes], + excludedTypes: [ + UITypes.Checkbox, + UITypes.SingleSelect, + UITypes.MultiSelect, + UITypes.Collaborator, + UITypes.Attachment, + ...numericUITypes, + ], }, { text: 'is null', From 7b98e30a8c6c9bc0c3b50a280e9c4672e7ee9ac9 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 12:18:04 +0800 Subject: [PATCH 020/251] feat(nc-gui): exclude operators for LinkToAnotherRecord --- packages/nc-gui/utils/filterUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nc-gui/utils/filterUtils.ts b/packages/nc-gui/utils/filterUtils.ts index df42fb033e..080d4a9c5e 100644 --- a/packages/nc-gui/utils/filterUtils.ts +++ b/packages/nc-gui/utils/filterUtils.ts @@ -51,6 +51,7 @@ export const comparisonOpList: { UITypes.MultiSelect, UITypes.Collaborator, UITypes.Attachment, + UITypes.LinkToAnotherRecord, ...numericUITypes, ], }, @@ -64,6 +65,7 @@ export const comparisonOpList: { UITypes.MultiSelect, UITypes.Collaborator, UITypes.Attachment, + UITypes.LinkToAnotherRecord, ...numericUITypes, ], }, From 8840cd9d438ae3e60b7d25325207a55e482b5a51 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 12:59:14 +0800 Subject: [PATCH 021/251] fix(nocodb): condition notempty logic --- .../nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts index dca95c9a0e..2711fc881e 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts @@ -436,7 +436,7 @@ const parseConditionV2 = async ( if (column.uidt === UITypes.Formula) { [field, val] = [val, field]; } - qb = qb.whereNot(field, val); + qb = qb.whereNot(field, val).orWhereNull(field); break; case 'null': qb = qb.whereNull(customWhereClause || field); From f2b224b964689ff9f296a594ca3cc9db041c93f1 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 14:11:13 +0800 Subject: [PATCH 022/251] feat(nocodb): handle case sensitive case for mysql --- .../db/sql-data-mapper/lib/sql/conditionV2.ts | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts index 2711fc881e..c09199258a 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts @@ -273,15 +273,29 @@ const parseConditionV2 = async ( let [field, val] = [_field, _val]; switch (filter.comparison_op) { case 'eq': - qb = qb.where(field, val); + if (qb?.client?.config?.client === 'mysql2') { + // mysql is case-insensitive, turn to case-sensitive + qb = qb.whereRaw('BINARY ?? = ?', [field, val]); + } else { + qb = qb.where(field, val); + } break; case 'neq': case 'not': - qb = qb.where((nestedQb) => { - nestedQb - .whereNot(field, val) - .orWhereNull(customWhereClause ? _val : _field); - }); + if (qb?.client?.config?.client === 'mysql2') { + // mysql is case-insensitive, turn to case-sensitive + qb = qb.where((nestedQb) => { + nestedQb + .whereRaw('BINARY ?? != ?', [field, val]) + .orWhereNull(customWhereClause ? _val : _field); + }); + } else { + qb = qb.where((nestedQb) => { + nestedQb + .whereNot(field, val) + .orWhereNull(customWhereClause ? _val : _field); + }); + } break; case 'like': if (!val) { From bd28bb837a930657d5bc3afa79c26fe0f534e147 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 2 Feb 2023 14:34:53 +0800 Subject: [PATCH 023/251] feat(nc-gui): display filter operator text conditionally --- .../smartsheet/toolbar/ColumnFilter.vue | 4 +--- packages/nc-gui/utils/filterUtils.ts | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue index 54456dcf3c..58b90d1d2f 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue @@ -215,7 +215,6 @@ defineExpose({ {{ op.text }} - - -