From eb12c2f63c658c8c97536d71abf889df16968d93 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 12:50:31 +0530 Subject: [PATCH 01/11] fix: allow arrow navigation with multi select Signed-off-by: Pranav C --- packages/nc-gui/components/cell/MultiSelect.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index dcfd660be9..35a055c293 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -171,7 +171,14 @@ useSelectedCellKeyupListener(active, (e) => { isOpen.value = true } break + case 'ArrowUp': + case 'ArrowDown': + case 'ArrowRight': + case 'ArrowLeft': + // skip + break default: + e.stopPropagation() isOpen.value = true break } @@ -248,7 +255,7 @@ const onTagClick = (e: Event, onClose: Function) => { :class="{ '!ml-[-8px]': readOnly }" :dropdown-class-name="`nc-dropdown-multi-select-cell ${isOpen ? 'active' : ''}`" @search="search" - @keydown.stop + @keydown.enter.stop @click="isOpen = (active || editable) && !isOpen" > Date: Tue, 22 Nov 2022 12:52:53 +0530 Subject: [PATCH 02/11] refactor(gui): show options validation outside the scrollable section Signed-off-by: Pranav C --- .../components/smartsheet/column/SelectOptions.vue | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/column/SelectOptions.vue b/packages/nc-gui/components/smartsheet/column/SelectOptions.vue index f02646718b..8ddefd56a2 100644 --- a/packages/nc-gui/components/smartsheet/column/SelectOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/SelectOptions.vue @@ -174,13 +174,12 @@ watch(inputs, () => { /> - + +
+ {{ validateInfos['colOptions.options'].help[0][0] }} +
From a19564d9dd7975f34a5ecb330d5f18385a4b97bc Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 13:02:37 +0530 Subject: [PATCH 03/11] refactor(gui): show validation error in toast Signed-off-by: Pranav C --- packages/nc-gui/composables/useColumnCreateStore.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/composables/useColumnCreateStore.ts b/packages/nc-gui/composables/useColumnCreateStore.ts index 922b12464c..8ca81c579e 100644 --- a/packages/nc-gui/composables/useColumnCreateStore.ts +++ b/packages/nc-gui/composables/useColumnCreateStore.ts @@ -197,7 +197,16 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState } catch (e) { console.log(e) console.trace() - message.error(t('msg.error.formValidationFailed')) + + const errorMsgs = e.errorFields + ?.map((e: any) => e.errors?.join(', ')) + .filter(Boolean) + .join(', ') + if (errorMsgs) { + message.error(errorMsgs) + } else { + message.error(t('msg.error.formValidationFailed')) + } return } From f999478ebeb936537e06e74118377577ed1b1dc9 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 13:09:00 +0530 Subject: [PATCH 04/11] refactor(gui): highlight active option in dropdown Signed-off-by: Pranav C --- packages/nc-gui/assets/style.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss index b5551d6076..1a65ac5e13 100644 --- a/packages/nc-gui/assets/style.scss +++ b/packages/nc-gui/assets/style.scss @@ -281,3 +281,8 @@ a { .ant-modal { @apply !top-[50px]; } + + +.ant-select-item-option-active:not(.ant-select-item-option-disabled) { + @apply bg-primary bg-opacity-20; +} From 913a3ba80040d02bd5bd1f5438677da51e415de9 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 13:22:43 +0530 Subject: [PATCH 05/11] fix(gui): skip delete key and toggle options list only if char key pressed Signed-off-by: Pranav C --- .../nc-gui/components/cell/MultiSelect.vue | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 35a055c293..ec2d078b87 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -107,13 +107,13 @@ const selectedTitles = computed(() => ? typeof modelValue === 'string' ? isMysql ? modelValue.split(',').sort((a, b) => { - const opa = options.value.find((el) => el.title === a) - const opb = options.value.find((el) => el.title === b) - if (opa && opb) { - return opa.order! - opb.order! - } - return 0 - }) + const opa = options.value.find((el) => el.title === a) + const opb = options.value.find((el) => el.title === b) + if (opa && opb) { + return opa.order! - opb.order! + } + return 0 + }) : modelValue.split(',') : modelValue : [], @@ -175,11 +175,15 @@ useSelectedCellKeyupListener(active, (e) => { case 'ArrowDown': case 'ArrowRight': case 'ArrowLeft': + case 'Delete': // skip break default: - e.stopPropagation() - isOpen.value = true + // toggle only if char key pressed + if (e.key?.length === 1) { + e.stopPropagation() + isOpen.value = true + } break } }) From 043cbcf4e7d7e478ac8f024175a2552876c9a714 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 13:29:12 +0530 Subject: [PATCH 06/11] fix(gui): block event bubbling Signed-off-by: Pranav C --- packages/nc-gui/components/cell/MultiSelect.vue | 2 +- packages/nc-gui/composables/useColumnCreateStore.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index ec2d078b87..bb4e6b86e4 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -259,7 +259,7 @@ const onTagClick = (e: Event, onClose: Function) => { :class="{ '!ml-[-8px]': readOnly }" :dropdown-class-name="`nc-dropdown-multi-select-cell ${isOpen ? 'active' : ''}`" @search="search" - @keydown.enter.stop + @keydown.stop @click="isOpen = (active || editable) && !isOpen" > e.errors?.join(', ')) .filter(Boolean) From 523105c8b0878996d38b38fe2be304a69e0bdebc Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 13:33:26 +0530 Subject: [PATCH 07/11] fix(gui): allow typing directly in single select Signed-off-by: Pranav C --- packages/nc-gui/components/cell/SingleSelect.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index ab20e724a8..f4e93c04f5 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -102,6 +102,13 @@ useSelectedCellKeyupListener(active, (e) => { isOpen.value = true } break + default: + // toggle only if char key pressed + if (e.key?.length === 1) { + e.stopPropagation() + isOpen.value = true + } + break } }) From 0c78030fbe028d6b3c0da1d227cfb1ff4aefeb2a Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 14:37:22 +0530 Subject: [PATCH 08/11] fix(gui): email validation correction Signed-off-by: Pranav C --- packages/nc-gui/components/account/UsersModal.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/components/account/UsersModal.vue b/packages/nc-gui/components/account/UsersModal.vue index 14665b8561..2eab6b1118 100644 --- a/packages/nc-gui/components/account/UsersModal.vue +++ b/packages/nc-gui/components/account/UsersModal.vue @@ -4,7 +4,7 @@ import { Form, computed, extractSdkResponseErrorMsg, - isEmail, + validateEmail, message, ref, useCopy, @@ -52,7 +52,7 @@ const validators = computed(() => { callback('Email is required') return } - const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !isEmail(e)) + const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e)) if (invalidEmails.length > 0) { callback(`${invalidEmails.length > 1 ? ' Invalid emails:' : 'Invalid email:'} ${invalidEmails.join(', ')} `) } else { From b25507afb97b4487ea8c37dd4db9678316b5e046 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 14:57:21 +0530 Subject: [PATCH 09/11] fix(gui): super admin user list pagination Signed-off-by: Pranav C --- packages/nc-gui/components/account/UserList.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/components/account/UserList.vue b/packages/nc-gui/components/account/UserList.vue index a6ac569901..d57a166f4d 100644 --- a/packages/nc-gui/components/account/UserList.vue +++ b/packages/nc-gui/components/account/UserList.vue @@ -29,7 +29,9 @@ const searchText = ref('') const pagination = reactive({ total: 0, pageSize: 10, + position: ['bottomCenter'] }) + const loadUsers = async (page = currentPage, limit = currentLimit) => { currentPage = page try { @@ -158,7 +160,7 @@ const copyPasswordResetUrl = async (user: User) => { Date: Tue, 22 Nov 2022 15:23:47 +0530 Subject: [PATCH 10/11] fix(gui): remove unnecessary chars from column default value Signed-off-by: Pranav C --- .../nc-gui/components/account/UserList.vue | 2 +- .../nc-gui/components/account/UsersModal.vue | 2 +- .../nc-gui/components/cell/MultiSelect.vue | 41 +++++++++++++------ .../nc-gui/components/cell/SingleSelect.vue | 25 +++++++++-- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/packages/nc-gui/components/account/UserList.vue b/packages/nc-gui/components/account/UserList.vue index d57a166f4d..4ce5764c3e 100644 --- a/packages/nc-gui/components/account/UserList.vue +++ b/packages/nc-gui/components/account/UserList.vue @@ -29,7 +29,7 @@ const searchText = ref('') const pagination = reactive({ total: 0, pageSize: 10, - position: ['bottomCenter'] + position: ['bottomCenter'], }) const loadUsers = async (page = currentPage, limit = currentLimit) => { diff --git a/packages/nc-gui/components/account/UsersModal.vue b/packages/nc-gui/components/account/UsersModal.vue index 2eab6b1118..3479fb6e8f 100644 --- a/packages/nc-gui/components/account/UsersModal.vue +++ b/packages/nc-gui/components/account/UsersModal.vue @@ -4,13 +4,13 @@ import { Form, computed, extractSdkResponseErrorMsg, - validateEmail, message, ref, useCopy, useDashboard, useI18n, useNuxtApp, + validateEmail, } from '#imports' import type { User } from '~/lib' import { Role } from '~/lib' diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index bb4e6b86e4..4ddf85d24d 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -33,8 +33,6 @@ const { modelValue } = defineProps() const emit = defineEmits(['update:modelValue']) -const { isMysql } = useProject() - const column = inject(ColumnInj)! const readOnly = inject(ReadonlyInj)! @@ -59,6 +57,8 @@ const { $api } = useNuxtApp() const { getMeta } = useMetas() +const { isPg, isMysql } = useProject() + // a variable to keep newly created options value // temporary until it's add the option to column meta const tempSelectedOptsState = reactive([]) @@ -107,13 +107,13 @@ const selectedTitles = computed(() => ? typeof modelValue === 'string' ? isMysql ? modelValue.split(',').sort((a, b) => { - const opa = options.value.find((el) => el.title === a) - const opb = options.value.find((el) => el.title === b) - if (opa && opb) { - return opa.order! - opb.order! - } - return 0 - }) + const opa = options.value.find((el) => el.title === a) + const opb = options.value.find((el) => el.title === b) + if (opa && opb) { + return opa.order! - opb.order! + } + return 0 + }) : modelValue.split(',') : modelValue : [], @@ -206,9 +206,26 @@ async function addIfMissingAndSave() { }) column.value.colOptions = { options: newOptions.map(({ value: _, ...rest }) => rest) } - await $api.dbTableColumn.update((column.value as { fk_column_id?: string })?.fk_column_id || (column.value?.id as string), { - ...column.value, - }) + const updatedColMeta = { ...column.value } + + // todo: refactor and avoid repetition + // 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.value) { + updatedColMeta.cdf = updatedColMeta.cdf.substring( + updatedColMeta.cdf.indexOf(`'`) + 1, + updatedColMeta.cdf.lastIndexOf(`'`), + ) + } + + // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped + if (!isMysql.value) { + updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") + } + + await $api.dbTableColumn.update( + (column.value as { fk_column_id?: string })?.fk_column_id || (column.value?.id as string), + updatedColMeta, + ) activeOptCreateInProgress.value-- if (!activeOptCreateInProgress.value) { diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index f4e93c04f5..e75739aeb7 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -49,6 +49,8 @@ const searchVal = ref() const { getMeta } = useMetas() +const { isPg, isMysql } = useProject() + // a variable to keep newly created option value // temporary until it's add the option to column meta const tempSelectedOptState = ref() @@ -127,9 +129,26 @@ async function addIfMissingAndSave() { }) column.value.colOptions = { options: options.value.map(({ value: _, ...rest }) => rest) } - await $api.dbTableColumn.update((column.value as { fk_column_id?: string })?.fk_column_id || (column.value?.id as string), { - ...column.value, - }) + const updatedColMeta = { ...column.value } + + // todo: refactor and avoid repetition + // 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.value) { + updatedColMeta.cdf = updatedColMeta.cdf.substring( + updatedColMeta.cdf.indexOf(`'`) + 1, + updatedColMeta.cdf.lastIndexOf(`'`), + ) + } + + // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped + if (!isMysql.value) { + updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") + } + + await $api.dbTableColumn.update( + (column.value as { fk_column_id?: string })?.fk_column_id || (column.value?.id as string), + updatedColMeta, + ) vModel.value = newOptValue await getMeta(column.value.fk_model_id!, true) } catch (e) { From 12a95968ba71584fb36fe9af506cabedfc9d0795 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 22 Nov 2022 16:08:39 +0530 Subject: [PATCH 11/11] fix(gui): check default value defined or not Signed-off-by: Pranav C --- .../nc-gui/components/cell/MultiSelect.vue | 24 ++++++++++--------- .../nc-gui/components/cell/SingleSelect.vue | 24 ++++++++++--------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/nc-gui/components/cell/MultiSelect.vue b/packages/nc-gui/components/cell/MultiSelect.vue index 4ddf85d24d..1b59039051 100644 --- a/packages/nc-gui/components/cell/MultiSelect.vue +++ b/packages/nc-gui/components/cell/MultiSelect.vue @@ -209,17 +209,19 @@ async function addIfMissingAndSave() { const updatedColMeta = { ...column.value } // todo: refactor and avoid repetition - // 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.value) { - updatedColMeta.cdf = updatedColMeta.cdf.substring( - updatedColMeta.cdf.indexOf(`'`) + 1, - updatedColMeta.cdf.lastIndexOf(`'`), - ) - } - - // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped - if (!isMysql.value) { - updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") + 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.value) { + updatedColMeta.cdf = updatedColMeta.cdf.substring( + updatedColMeta.cdf.indexOf(`'`) + 1, + updatedColMeta.cdf.lastIndexOf(`'`), + ) + } + + // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped + if (!isMysql.value) { + updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") + } } await $api.dbTableColumn.update( diff --git a/packages/nc-gui/components/cell/SingleSelect.vue b/packages/nc-gui/components/cell/SingleSelect.vue index e75739aeb7..b89bad6bc2 100644 --- a/packages/nc-gui/components/cell/SingleSelect.vue +++ b/packages/nc-gui/components/cell/SingleSelect.vue @@ -132,17 +132,19 @@ async function addIfMissingAndSave() { const updatedColMeta = { ...column.value } // todo: refactor and avoid repetition - // 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.value) { - updatedColMeta.cdf = updatedColMeta.cdf.substring( - updatedColMeta.cdf.indexOf(`'`) + 1, - updatedColMeta.cdf.lastIndexOf(`'`), - ) - } - - // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped - if (!isMysql.value) { - updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") + 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.value) { + updatedColMeta.cdf = updatedColMeta.cdf.substring( + updatedColMeta.cdf.indexOf(`'`) + 1, + updatedColMeta.cdf.lastIndexOf(`'`), + ) + } + + // Mysql escapes single quotes with backslash so we keep quotes but others have to unescaped + if (!isMysql.value) { + updatedColMeta.cdf = updatedColMeta.cdf.replace(/''/g, "'") + } } await $api.dbTableColumn.update(