-
+
- {
- if (!skipRowRemovalOnCancel) removeRowIfNew(expandedFormRow)
- }
- "
+ @update:model-value="!skipRowRemovalOnCancel && removeRowIfNew(expandedFormRow)"
/>
-
-import { computed, inject } from '#imports'
-import { ChangePageInj, PaginationDataInj } from '~/context'
+import { ChangePageInj, PaginationDataInj, computed, inject } from '#imports'
const paginatedData = inject(PaginationDataInj)!
diff --git a/packages/nc-gui/components/smartsheet/Row.vue b/packages/nc-gui/components/smartsheet/Row.vue
index acfbbea0c8..9950f567b8 100644
--- a/packages/nc-gui/components/smartsheet/Row.vue
+++ b/packages/nc-gui/components/smartsheet/Row.vue
@@ -1,15 +1,25 @@
-
+
diff --git a/packages/nc-gui/components/smartsheet-column/FormulaOptions.vue b/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue
similarity index 99%
rename from packages/nc-gui/components/smartsheet-column/FormulaOptions.vue
rename to packages/nc-gui/components/smartsheet/column/FormulaOptions.vue
index 186c477713..09d06566ca 100644
--- a/packages/nc-gui/components/smartsheet-column/FormulaOptions.vue
+++ b/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue
@@ -16,15 +16,16 @@ import {
onMounted,
useColumnCreateStoreOrThrow,
useDebounceFn,
+ useVModel,
validateDateWithUnknownFormat,
} from '#imports'
-interface Props {
- value: Record
-}
+const props = defineProps<{
+ value: any
+}>()
-const props = defineProps()
const emit = defineEmits(['update:value'])
+
const vModel = useVModel(props, 'value', emit)
const { setAdditionalValidations, validateInfos, sqlUi, column } = useColumnCreateStoreOrThrow()
@@ -619,6 +620,7 @@ onMounted(() => {
@change="handleInputDeb"
/>
+
Hint: Use {} to reference columns, e.g: {column_name}. For more, please check out
@@ -644,16 +646,19 @@ onMounted(() => {
{{ item.text }}
+
{{ item.description }}
Syntax:
{{ item.syntax }}
Examples:
+
({{ idx + 1 }}): {{ example }}
+
@@ -663,7 +668,9 @@ onMounted(() => {
+
+
diff --git a/packages/nc-gui/components/smartsheet-column/LinkedToAnotherRecordOptions.vue b/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
similarity index 97%
rename from packages/nc-gui/components/smartsheet-column/LinkedToAnotherRecordOptions.vue
rename to packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
index 288691f1f0..9596c76b0f 100644
--- a/packages/nc-gui/components/smartsheet-column/LinkedToAnotherRecordOptions.vue
+++ b/packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
@@ -1,15 +1,15 @@
- {
- clickCount = clickCount + 1
- debug = clickCount >= 4
- }
- "
- >
+
-
+
-
+
-
+
-
+
diff --git a/packages/nc-gui/components/smartsheet-toolbar/AddRow.vue b/packages/nc-gui/components/smartsheet/toolbar/AddRow.vue
similarity index 100%
rename from packages/nc-gui/components/smartsheet-toolbar/AddRow.vue
rename to packages/nc-gui/components/smartsheet/toolbar/AddRow.vue
diff --git a/packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
similarity index 81%
rename from packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue
rename to packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
index db3fb430ed..0edc2503aa 100644
--- a/packages/nc-gui/components/smartsheet-toolbar/ColumnFilter.vue
+++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
@@ -1,7 +1,6 @@
@@ -131,6 +130,7 @@ const getIcon = (c: ColumnType) =>
+
+
{{ $t('general.showAll') }}
+
{{ $t('general.hideAll') }}
diff --git a/packages/nc-gui/components/smartsheet-toolbar/LockType.vue b/packages/nc-gui/components/smartsheet/toolbar/LockType.vue
similarity index 96%
rename from packages/nc-gui/components/smartsheet-toolbar/LockType.vue
rename to packages/nc-gui/components/smartsheet/toolbar/LockType.vue
index 947606bfed..2744f2dd11 100644
--- a/packages/nc-gui/components/smartsheet-toolbar/LockType.vue
+++ b/packages/nc-gui/components/smartsheet/toolbar/LockType.vue
@@ -2,8 +2,8 @@
import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
import MdiAccountIcon from '~icons/mdi/account'
import MdiAccountGroupIcon from '~icons/mdi/account-group'
-
import { LockType } from '~/lib'
+import { ActiveViewInj, inject } from '#imports'
const { type, hideTick } = defineProps<{ hideTick?: boolean; type: LockType }>()
@@ -35,11 +35,15 @@ const selectedView = inject(ActiveViewInj)
+
+
+
{{ $t(types[type].title) }}
+
{{ $t(types[type].subtitle) }}
diff --git a/packages/nc-gui/components/smartsheet-toolbar/MoreActions.vue b/packages/nc-gui/components/smartsheet/toolbar/MoreActions.vue
similarity index 92%
rename from packages/nc-gui/components/smartsheet-toolbar/MoreActions.vue
rename to packages/nc-gui/components/smartsheet/toolbar/MoreActions.vue
index f322e5f39b..6ef6335d71 100644
--- a/packages/nc-gui/components/smartsheet-toolbar/MoreActions.vue
+++ b/packages/nc-gui/components/smartsheet/toolbar/MoreActions.vue
@@ -1,10 +1,6 @@
@@ -12,8 +12,9 @@ const selectedView = inject(ActiveViewInj)
class="nc-view-icon group-hover:hidden"
:style="{ color: viewIcons[selectedView?.type].color }"
/>
- {{
- selectedView?.title
- }}
+
+
+ {{ selectedView?.title }}
+
diff --git a/packages/nc-gui/components/tabs/Auth.vue b/packages/nc-gui/components/tabs/Auth.vue
index 0d04cd4ddd..e18450d5b7 100644
--- a/packages/nc-gui/components/tabs/Auth.vue
+++ b/packages/nc-gui/components/tabs/Auth.vue
@@ -1,13 +1,11 @@
-
+
@@ -46,7 +43,13 @@ const selectedTab = $computed(() => tabsInfo[selectedTabKey])
-
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/components/tabs/Smartsheet.vue b/packages/nc-gui/components/tabs/Smartsheet.vue
index a9115c0f53..c4a8edda3e 100644
--- a/packages/nc-gui/components/tabs/Smartsheet.vue
+++ b/packages/nc-gui/components/tabs/Smartsheet.vue
@@ -1,6 +1,5 @@
diff --git a/packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue b/packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
index 5d1713d3ed..8062be670a 100644
--- a/packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
+++ b/packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
@@ -1,6 +1,5 @@
@@ -31,12 +31,15 @@ const urls = computed(() => replaceUrlsWithLink(result.value))
{{ column.colOptions.error }}
+
ERR!
+
{{ result }}
+
Warning: Formula fields should be configured in the field menu dropdown.
diff --git a/packages/nc-gui/components/virtual-cell/HasMany.vue b/packages/nc-gui/components/virtual-cell/HasMany.vue
index d9298241b9..a68d5c0c9d 100644
--- a/packages/nc-gui/components/virtual-cell/HasMany.vue
+++ b/packages/nc-gui/components/virtual-cell/HasMany.vue
@@ -10,7 +10,6 @@ import {
ReloadRowDataHookInj,
RowInj,
computed,
- defineAsyncComponent,
inject,
ref,
useProvideLTARStore,
@@ -18,12 +17,6 @@ import {
useUIPermission,
} from '#imports'
-const ItemChip = defineAsyncComponent(() => import('./components/ItemChip.vue'))
-
-const ListItems = defineAsyncComponent(() => import('./components/ListItems.vue'))
-
-const ListChildItems = defineAsyncComponent(() => import('./components/ListChildItems.vue'))
-
const column = inject(ColumnInj)!
const cellValue = inject(CellValueInj)!
@@ -52,6 +45,7 @@ const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvid
isNew,
reloadRowTrigger.trigger,
)
+
await loadRelatedTableMeta()
const localCellValue = computed (() => {
@@ -94,17 +88,26 @@ const onAttachRecord = () => {
-
+
+
more...
+
+
{
-
+
-
+
diff --git a/packages/nc-gui/components/virtual-cell/Lookup.vue b/packages/nc-gui/components/virtual-cell/Lookup.vue
index d8a01a49b6..0cb9ca580c 100644
--- a/packages/nc-gui/components/virtual-cell/Lookup.vue
+++ b/packages/nc-gui/components/virtual-cell/Lookup.vue
@@ -56,7 +56,7 @@ const lookupColumnMetaProps = useColumn(lookupColumn)
-
-
+
@@ -82,7 +82,7 @@ const lookupColumnMetaProps = useColumn(lookupColumn)
),
}"
>
-
+
diff --git a/packages/nc-gui/components/virtual-cell/ManyToMany.vue b/packages/nc-gui/components/virtual-cell/ManyToMany.vue
index 0bb553d693..852ff7c01a 100644
--- a/packages/nc-gui/components/virtual-cell/ManyToMany.vue
+++ b/packages/nc-gui/components/virtual-cell/ManyToMany.vue
@@ -10,6 +10,7 @@ import {
ReloadRowDataHookInj,
RowInj,
computed,
+ createEventHook,
inject,
ref,
useProvideLTARStore,
@@ -17,12 +18,6 @@ import {
useUIPermission,
} from '#imports'
-const ItemChip = defineAsyncComponent(() => import('./components/ItemChip.vue'))
-
-const ListItems = defineAsyncComponent(() => import('./components/ListItems.vue'))
-
-const ListChildItems = defineAsyncComponent(() => import('./components/ListChildItems.vue'))
-
const column = inject(ColumnInj)!
const row = inject(RowInj)!
@@ -44,6 +39,7 @@ const childListDlg = ref(false)
const { isUIAllowed } = useUIPermission()
const { state, isNew, removeLTARRef } = useSmartsheetRowStoreOrThrow()
+
const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore(
column as Ref >,
row,
@@ -93,7 +89,13 @@ const onAttachRecord = () => {
-
+
more...
@@ -113,9 +115,13 @@ const onAttachRecord = () => {
-
+
-
+
diff --git a/packages/nc-gui/components/virtual-cell/components/ItemChip.vue b/packages/nc-gui/components/virtual-cell/components/ItemChip.vue
index 4cb81f0865..fe11b08950 100644
--- a/packages/nc-gui/components/virtual-cell/components/ItemChip.vue
+++ b/packages/nc-gui/components/virtual-cell/components/ItemChip.vue
@@ -1,14 +1,5 @@
@@ -34,8 +33,9 @@ async function editHook(hook: Record) {
>
-
-
+
+
+
diff --git a/packages/nc-gui/components/webhook/Editor.vue b/packages/nc-gui/components/webhook/Editor.vue
index 99372b95e7..61e302cba9 100644
--- a/packages/nc-gui/components/webhook/Editor.vue
+++ b/packages/nc-gui/components/webhook/Editor.vue
@@ -1,19 +1,30 @@
@@ -413,6 +425,7 @@ onMounted(async () => {
{{ meta.title }} : {{ hook.title || 'Webhooks' }}
+
@@ -421,6 +434,7 @@ onMounted(async () => {
Test Webhook
+
@@ -430,7 +444,9 @@ onMounted(async () => {
+
+
@@ -445,6 +461,7 @@ onMounted(async () => {
+
@@ -461,6 +478,7 @@ onMounted(async () => {
+
{
class="nc-select-hook-url-method"
dropdown-class-name="nc-dropdown-hook-notification-url-method"
>
- {{ method.title }}
+
+ {{ method.title }}
+
@@ -523,16 +543,20 @@ onMounted(async () => {
-
+
+
-
+
+
+
-
+
+
For more about auth option refer
axios docs.
@@ -545,7 +569,7 @@ onMounted(async () => {
- {
- {
- {
- {
+
@@ -612,8 +637,15 @@ onMounted(async () => {
- On Condition
-
+ On Condition
+
+
+ {
-
-import { message } from 'ant-design-vue'
-import { MetaInj, extractSdkResponseErrorMsg, inject, onMounted, ref, useI18n, useNuxtApp } from '#imports'
+import { MetaInj, extractSdkResponseErrorMsg, inject, message, onMounted, ref, useI18n, useNuxtApp } from '#imports'
const emit = defineEmits(['edit', 'add'])
@@ -54,6 +53,7 @@ onMounted(() => {
{{ meta.title }} : Webhooks
+
{
{{ $t('activity.addWebhook') }}
+
+
@@ -73,21 +75,25 @@ onMounted(() => {
{{ item.event }} {{ item.operation }}
+
{{ item.title }}
+
+
{{ $t('labels.notifyVia') }} : {{ item?.notification?.type }}
+
@@ -97,6 +103,7 @@ onMounted(() => {
+
Webhooks list is empty, create new webhook by clicking 'Create webhook' button.
diff --git a/packages/nc-gui/components/webhook/Test.vue b/packages/nc-gui/components/webhook/Test.vue
index d9e6455d43..058e27fef6 100644
--- a/packages/nc-gui/components/webhook/Test.vue
+++ b/packages/nc-gui/components/webhook/Test.vue
@@ -1,6 +1,5 @@
@@ -325,7 +334,7 @@ onBeforeUnmount(reset)
-
+
+
-
+
+
@@ -367,8 +379,10 @@ onBeforeUnmount(reset)
{{ $t('labels.accentColor') }}
+
-
+
+
@@ -393,7 +407,7 @@ onBeforeUnmount(reset)
-
+
@@ -416,7 +430,8 @@ onBeforeUnmount(reset)
-
+
+
@@ -472,16 +487,16 @@ onBeforeUnmount(reset)
-
+
-
+
-
+
diff --git a/packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue b/packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
index 397d7eec5f..360f0c8f0b 100644
--- a/packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
+++ b/packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
@@ -1,12 +1,7 @@
-
-
+
-
diff --git a/packages/nc-gui/pages/[projectType]/[projectId]/index/index/auth.vue b/packages/nc-gui/pages/[projectType]/[projectId]/index/index/auth.vue
index 0073f0c16f..dd3d59cd47 100644
--- a/packages/nc-gui/pages/[projectType]/[projectId]/index/index/auth.vue
+++ b/packages/nc-gui/pages/[projectType]/[projectId]/index/index/auth.vue
@@ -1,7 +1,3 @@
-
-
-
+
diff --git a/packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue b/packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue
index 5cbdbf4492..3c793a71ba 100644
--- a/packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue
+++ b/packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue
@@ -1,9 +1,17 @@
@@ -63,7 +55,10 @@ function resetError() {
-
+
{{ $t('title.resetPassword') }}
diff --git a/packages/nc-gui/pages/index/apps.vue b/packages/nc-gui/pages/index/apps.vue
index 5b3d81bf2c..6780d02668 100644
--- a/packages/nc-gui/pages/index/apps.vue
+++ b/packages/nc-gui/pages/index/apps.vue
@@ -1,6 +1,6 @@
@@ -11,7 +13,7 @@ const route = useRoute()
>
@@ -23,11 +25,11 @@ const route = useRoute()
-
+
-
+
diff --git a/packages/nc-gui/pages/index/index/[projectId].vue b/packages/nc-gui/pages/index/index/[projectId].vue
index 055691fa37..d8d529348a 100644
--- a/packages/nc-gui/pages/index/index/[projectId].vue
+++ b/packages/nc-gui/pages/index/index/[projectId].vue
@@ -1,27 +1,25 @@
-
+
@@ -194,18 +195,12 @@ const customRow = (record: ProjectType) => ({
-
-
+
+
-
+
@@ -231,12 +226,13 @@ const customRow = (record: ProjectType) => ({
-
+
+
-
+
+
@@ -271,7 +269,7 @@ const customRow = (record: ProjectType) => ({
-
+
diff --git a/packages/nc-gui/pages/index/user/index.vue b/packages/nc-gui/pages/index/user/index.vue
index f8eacfa737..8684d74738 100644
--- a/packages/nc-gui/pages/index/user/index.vue
+++ b/packages/nc-gui/pages/index/user/index.vue
@@ -1,5 +1,146 @@
+
+
-
+
+
+
+ {{ $t('activity.changePwd') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/pages/index/user/index/index.vue b/packages/nc-gui/pages/index/user/index/index.vue
deleted file mode 100644
index ecf7834b47..0000000000
--- a/packages/nc-gui/pages/index/user/index/index.vue
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
-
-
-
- {{ $t('activity.changePwd') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/signin.vue b/packages/nc-gui/pages/signin.vue
index 86638e9c13..e826fc7ae2 100644
--- a/packages/nc-gui/pages/signin.vue
+++ b/packages/nc-gui/pages/signin.vue
@@ -1,35 +1,22 @@
@@ -84,7 +67,10 @@ function resetError() {
-
+
{{ $t('general.signIn') }}
diff --git a/packages/nc-gui/pages/signup/[[token]].vue b/packages/nc-gui/pages/signup/[[token]].vue
index 8e39c7de84..52e6905ca4 100644
--- a/packages/nc-gui/pages/signup/[[token]].vue
+++ b/packages/nc-gui/pages/signup/[[token]].vue
@@ -1,22 +1,9 @@
@@ -105,7 +85,10 @@ function resetError() {
-
+
{{ $t('general.signUp') }}
diff --git a/packages/nc-gui/plugins/a.dayjs.ts b/packages/nc-gui/plugins/a.dayjs.ts
index 261c3d5fda..76a1c35bc9 100644
--- a/packages/nc-gui/plugins/a.dayjs.ts
+++ b/packages/nc-gui/plugins/a.dayjs.ts
@@ -1,4 +1,4 @@
-import dayjs from 'dayjs'
+import { extend } from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime.js'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import duration from 'dayjs/plugin/duration.js'
@@ -7,9 +7,9 @@ import weekday from 'dayjs/plugin/weekday.js'
import { defineNuxtPlugin } from '#imports'
export default defineNuxtPlugin(() => {
- dayjs.extend(utc)
- dayjs.extend(relativeTime)
- dayjs.extend(customParseFormat)
- dayjs.extend(duration)
- dayjs.extend(weekday)
+ extend(utc)
+ extend(relativeTime)
+ extend(customParseFormat)
+ extend(duration)
+ extend(weekday)
})
diff --git a/packages/nc-gui/plugins/a.i18n.ts b/packages/nc-gui/plugins/a.i18n.ts
index 55cf0472fb..a0c9c2a498 100644
--- a/packages/nc-gui/plugins/a.i18n.ts
+++ b/packages/nc-gui/plugins/a.i18n.ts
@@ -1,6 +1,5 @@
-import { defineNuxtPlugin } from 'nuxt/app'
import { createI18n } from 'vue-i18n'
-import { nextTick } from 'vue'
+import { defineNuxtPlugin, nextTick } from '#imports'
import type { Language, NocoI18n } from '~/lib'
import { LanguageAlias } from '~/lib'
diff --git a/packages/nc-gui/plugins/initializeFeedbackForm.ts b/packages/nc-gui/plugins/feedbackForm.ts
similarity index 94%
rename from packages/nc-gui/plugins/initializeFeedbackForm.ts
rename to packages/nc-gui/plugins/feedbackForm.ts
index 314131afc7..5c6e58955f 100644
--- a/packages/nc-gui/plugins/initializeFeedbackForm.ts
+++ b/packages/nc-gui/plugins/feedbackForm.ts
@@ -1,8 +1,9 @@
import dayjs from 'dayjs'
-import { defineNuxtPlugin } from '#app'
+import { defineNuxtPlugin, useGlobal, useNuxtApp } from '#imports'
const handleFeedbackForm = async () => {
let { feedbackForm: currentFeedbackForm } = $(useGlobal())
+
if (!currentFeedbackForm) return
const { $api } = useNuxtApp()
@@ -10,6 +11,7 @@ const handleFeedbackForm = async () => {
const isFirstTimePolling = !currentFeedbackForm.lastFormPollDate
const now = dayjs()
+
const lastFormPolledDate = dayjs(currentFeedbackForm.lastFormPollDate)
if (isFirstTimePolling || dayjs.duration(now.diff(lastFormPolledDate)).days() > 0) {
diff --git a/packages/nc-gui/plugins/resizeDirective.ts b/packages/nc-gui/plugins/resizeDirective.ts
index d36a1396ca..549131ee99 100644
--- a/packages/nc-gui/plugins/resizeDirective.ts
+++ b/packages/nc-gui/plugins/resizeDirective.ts
@@ -1,4 +1,4 @@
-import { defineNuxtPlugin } from '#app'
+import { defineNuxtPlugin, getCurrentInstance } from '#imports'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('xc-ver-resize', {
@@ -18,6 +18,7 @@ export default defineNuxtPlugin((nuxtApp) => {
resizer.addEventListener('mousedown', initDrag, false)
const instance = getCurrentInstance()
+
const emit =
instance?.emit ??
((arg, data) => {
@@ -37,6 +38,8 @@ export default defineNuxtPlugin((nuxtApp) => {
document.documentElement.addEventListener('mouseup', stopDrag, false)
}
+ ;(el as any).initDrag = initDrag
+
let width: number | string
// emit event on dragging
@@ -55,5 +58,12 @@ export default defineNuxtPlugin((nuxtApp) => {
emit('xcresized')
}
},
+ beforeUnmount: (el: Element) => {
+ const resizer = el.querySelector('.resizer')
+
+ if (resizer) {
+ resizer.removeEventListener('mousedown', (el as any).initDrag, false)
+ }
+ },
})
})
diff --git a/packages/nc-gui/plugins/state.ts b/packages/nc-gui/plugins/state.ts
index 08cc6574c8..69384aadbe 100644
--- a/packages/nc-gui/plugins/state.ts
+++ b/packages/nc-gui/plugins/state.ts
@@ -17,7 +17,7 @@ import { Language, LanguageAlias } from '~/lib'
export default defineNuxtPlugin(async () => {
const state = useGlobal()
- const { api } = useApi()
+ const { api } = useApi({ useGlobalInstance: true })
let currentLang = state.lang.value
diff --git a/packages/nc-gui/plugins/tele.ts b/packages/nc-gui/plugins/tele.ts
index 481dd133f3..cd9c0b1ca1 100644
--- a/packages/nc-gui/plugins/tele.ts
+++ b/packages/nc-gui/plugins/tele.ts
@@ -1,7 +1,6 @@
-import { defineNuxtPlugin } from 'nuxt/app'
import type { Socket } from 'socket.io-client'
import io from 'socket.io-client'
-import type { UseGlobalReturn } from '~/composables/useGlobal/types'
+import { defineNuxtPlugin, useGlobal, useRoute, useRouter, watch } from '#imports'
// todo: ignore init if tele disabled
export default defineNuxtPlugin(async (nuxtApp) => {
@@ -59,6 +58,10 @@ export default defineNuxtPlugin(async (nuxtApp) => {
if (vnode.el) vnode.el.addEventListener('click', getListener(binding))
else el.addEventListener('click', getListener(binding))
},
+ beforeUnmount(el, binding, vnode) {
+ if (vnode.el) vnode.el.removeEventListener('click', getListener(binding))
+ else el.removeEventListener('click', getListener(binding))
+ },
})
function getListener(binding: any) {
@@ -75,7 +78,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
}
}
- watch((nuxtApp.$state as UseGlobalReturn).token, (newToken, oldToken) => {
+ watch((nuxtApp.$state as ReturnType).token, (newToken, oldToken) => {
if (newToken && newToken !== oldToken) init(newToken)
else if (!newToken) socket.disconnect()
})
diff --git a/packages/nc-gui/test/vite.config.ts b/packages/nc-gui/test/vite.config.ts
index 2c4d773b7a..89731cff35 100644
--- a/packages/nc-gui/test/vite.config.ts
+++ b/packages/nc-gui/test/vite.config.ts
@@ -18,10 +18,6 @@ export default defineConfig({
}),
],
test: {
- setupFiles: path.resolve(__dirname, './vuetify.config.js'),
- deps: {
- inline: ['vuetify'],
- },
globals: true,
environment: 'happy-dom',
},
diff --git a/packages/nc-gui/test/vuetify.config.js b/packages/nc-gui/test/vuetify.config.js
deleted file mode 100644
index 4aa920d287..0000000000
--- a/packages/nc-gui/test/vuetify.config.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/** fix issue with testing: https://github.com/vuetifyjs/vuetify/issues/14749 */
-global.CSS = { supports: () => false }
diff --git a/packages/nc-gui/utils/currencyUtils.ts b/packages/nc-gui/utils/currencyUtils.ts
index 5ac1ccb46f..0cdb4e2110 100644
--- a/packages/nc-gui/utils/currencyUtils.ts
+++ b/packages/nc-gui/utils/currencyUtils.ts
@@ -1,4 +1,4 @@
-import locale from 'locale-codes'
+import { all } from 'locale-codes'
export const currencyCodes = [
'AED',
@@ -191,24 +191,14 @@ export function validateCurrencyCode(v: string) {
}
export function currencyLocales() {
- const localeList = locale.all
- .filter((l: Record) => {
- try {
- if (Intl.NumberFormat.supportedLocalesOf(l.tag).length > 0) {
- return true
- }
- return false
- } catch (e) {
- return false
- }
- })
- .map((l: Record) => {
+ return all
+ .filter((l) => validateCurrencyLocale(l.tag))
+ .map((l) => {
return {
text: `${l.name} (${l.tag})`,
value: l.tag,
}
})
- return localeList
}
export function validateCurrencyLocale(v: string) {
diff --git a/packages/nc-gui/utils/generateName.ts b/packages/nc-gui/utils/generateName.ts
index ffd144cd51..9d60b74309 100644
--- a/packages/nc-gui/utils/generateName.ts
+++ b/packages/nc-gui/utils/generateName.ts
@@ -1,6 +1,6 @@
-import { adjectives, animals, starWars, uniqueNamesGenerator } from 'unique-names-generator'
+export const generateUniqueName = async () => {
+ const { adjectives, animals, starWars, uniqueNamesGenerator } = await import('unique-names-generator')
-export const generateUniqueName = () => {
return uniqueNamesGenerator({
dictionaries: [[starWars], [adjectives, animals]][Math.floor(Math.random() * 2)],
})
@@ -14,7 +14,7 @@ export const generateUniqueTitle = = Record {
let c = 1
- while (arr.some((item) => item[predicate] === (`${title}-${c}` as keyof T))) {
+ while (arr.some((item) => item[predicate].includes(`${title}-${c}` as keyof T))) {
c++
}
diff --git a/packages/nc-gui/utils/iconUtils.ts b/packages/nc-gui/utils/iconUtils.ts
index aff29794a7..1b437ee998 100644
--- a/packages/nc-gui/utils/iconUtils.ts
+++ b/packages/nc-gui/utils/iconUtils.ts
@@ -12,6 +12,29 @@ import MdiThumbUp from '~icons/mdi/thumb-up'
import MdiThumbUpOutline from '~icons/mdi/thumb-up-outline'
import MdiFlag from '~icons/mdi/flag'
import MdiFlagOutline from '~icons/mdi/flag-outline'
+import MdiTableLarge from '~icons/mdi/table-large'
+import MdiEyeCircleOutline from '~icons/mdi/eye-circle-outline'
+import MdiAccountGroup from '~icons/mdi/account-group'
+
+export const iconMap = {
+ 'mdi-check-bold': MdiCheckBold,
+ 'mdi-crop-square': MdiCropSquare,
+ 'mdi-check-circle-outline': MdiCheckCircleOutline,
+ 'mdi-checkbox-blank-circle-outline': MdiCheckboxBlankCircleOutline,
+ 'mdi-star': MdiStar,
+ 'mdi-star-outline': MdiStarOutline,
+ 'mdi-heart': MdiHeart,
+ 'mdi-heart-outline': MdiHeartOutline,
+ 'mdi-moon-full': MdiMoonFull,
+ 'mdi-moon-new': MdiMoonNew,
+ 'mdi-thumb-up': MdiThumbUp,
+ 'mdi-thumb-up-outline': MdiThumbUpOutline,
+ 'mdi-flag': MdiFlag,
+ 'mdi-flag-outline': MdiFlagOutline,
+ 'mdi-table-large': MdiTableLarge,
+ 'mdi-eye-circle-outline': MdiEyeCircleOutline,
+ 'mdi-account-group': MdiAccountGroup,
+} as const
export const getMdiIcon = (type: string): any => {
switch (type) {
@@ -43,5 +66,11 @@ export const getMdiIcon = (type: string): any => {
return MdiFlag
case 'mdi-flag-outline':
return MdiFlagOutline
+ case 'mdi-table-large':
+ return MdiTableLarge
+ case 'mdi-eye-circle-outline':
+ return MdiEyeCircleOutline
+ case 'mdi-account-group':
+ return MdiAccountGroup
}
}
diff --git a/packages/nc-gui/utils/parsers/CSVTemplateAdapter.ts b/packages/nc-gui/utils/parsers/CSVTemplateAdapter.ts
index 258992587f..f68c9be56e 100644
--- a/packages/nc-gui/utils/parsers/CSVTemplateAdapter.ts
+++ b/packages/nc-gui/utils/parsers/CSVTemplateAdapter.ts
@@ -1,4 +1,4 @@
-import Papaparse from 'papaparse'
+import { parse } from 'papaparse'
import TemplateGenerator from './TemplateGenerator'
export default class CSVTemplateAdapter extends TemplateGenerator {
@@ -24,7 +24,7 @@ export default class CSVTemplateAdapter extends TemplateGenerator {
}
async init() {
- this.csv = Papaparse.parse(this.csvData, { header: true })
+ this.csv = parse(this.csvData, { header: true })
}
parseData() {
diff --git a/packages/nc-gui/utils/parsers/ExcelTemplateAdapter.ts b/packages/nc-gui/utils/parsers/ExcelTemplateAdapter.ts
index 85c631d2a2..6e1b9ea4c9 100644
--- a/packages/nc-gui/utils/parsers/ExcelTemplateAdapter.ts
+++ b/packages/nc-gui/utils/parsers/ExcelTemplateAdapter.ts
@@ -1,9 +1,8 @@
-import XLSX from 'xlsx'
import { UITypes } from 'nocodb-sdk'
import TemplateGenerator from './TemplateGenerator'
import { getCheckboxValue, isCheckboxType } from './parserHelpers'
-const excelTypeToUidt: Record = {
+const excelTypeToUidt: Record = {
d: UITypes.DateTime,
b: UITypes.Checkbox,
n: UITypes.Number,
@@ -11,39 +10,59 @@ const excelTypeToUidt: Record = {
}
export default class ExcelTemplateAdapter extends TemplateGenerator {
- config: Record
+ config: {
+ maxRowsToParse: number
+ } & Record
+
name: string
+
excelData: any
- project: Record
- data: Record
+
+ project: {
+ title: string
+ tables: any[]
+ }
+
+ data: Record = {}
+
wb: any
+
+ xlsx: typeof import('xlsx')
+
constructor(name = '', data = {}, parserConfig = {}) {
super()
this.config = {
maxRowsToParse: 500,
...parserConfig,
}
+
this.name = name
+
this.excelData = data
+
this.project = {
title: this.name,
tables: [],
}
- this.data = {}
+
+ this.xlsx = {} as any
}
async init() {
- const options: Record = {
+ this.xlsx = await import('xlsx')
+
+ const options = {
cellText: true,
cellDates: true,
}
+
if (this.name.slice(-3) === 'csv') {
- this.wb = XLSX.read(new TextDecoder().decode(new Uint8Array(this.excelData)), {
+ this.wb = this.xlsx.read(new TextDecoder().decode(new Uint8Array(this.excelData)), {
type: 'string',
...options,
})
} else {
- this.wb = XLSX.read(new Uint8Array(this.excelData), {
+ this.wb = this.xlsx.read(new Uint8Array(this.excelData), {
type: 'array',
...options,
})
@@ -51,9 +70,10 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
}
parse() {
- const tableNamePrefixRef: any = {}
+ const tableNamePrefixRef: Record = {}
+
for (let i = 0; i < this.wb.SheetNames.length; i++) {
- const columnNamePrefixRef: Record = { id: 0 }
+ const columnNamePrefixRef: Record = { id: 0 }
const sheet: any = this.wb.SheetNames[i]
let tn: string = (sheet || 'table').replace(/[` ~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/g, '_').trim()
@@ -62,11 +82,11 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
}
tableNamePrefixRef[tn] = 0
- const table: Record = { table_name: tn, ref_table_name: tn, columns: [] }
+ const table = { table_name: tn, ref_table_name: tn, columns: [] as any[] }
this.data[tn] = []
const ws: any = this.wb.Sheets[sheet]
- const range = XLSX.utils.decode_range(ws['!ref'])
- let rows: any = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null })
+ const range = this.xlsx.utils.decode_range(ws['!ref'])
+ let rows: any = this.xlsx.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null })
if (this.name.slice(-3) !== 'csv') {
// fix precision bug & timezone offset issues introduced by xlsx
@@ -77,7 +97,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
const day_ms = 24 * 60 * 60 * 1000
// handle date1904 property
const fixImportedDate = (date: Date) => {
- const parsed = XLSX.SSF.parse_date_code((date.getTime() - dnthresh) / day_ms, {
+ const parsed = this.xlsx.SSF.parse_date_code((date.getTime() - dnthresh) / day_ms, {
date1904: this.wb.Workbook.WBProps.date1904,
})
return new Date(parsed.y, parsed.m, parsed.d, parsed.H, parsed.M, parsed.S)
@@ -111,7 +131,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
table.columns.push(column)
// const cellId = `${col.toString(26).split('').map(s => (parseInt(s, 26) + 10).toString(36).toUpperCase())}2`;
- const cellId = XLSX.utils.encode_cell({
+ const cellId = this.xlsx.utils.encode_cell({
c: range.s.c + col,
r: columnNameRowExist,
})
@@ -173,7 +193,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
}
if (
rows.slice(1, this.config.maxRowsToParse).every((v: any, i: any) => {
- const cellId = XLSX.utils.encode_cell({
+ const cellId = this.xlsx.utils.encode_cell({
c: range.s.c + col,
r: i + columnNameRowExist,
})
@@ -188,7 +208,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
} else if (column.uidt === UITypes.DateTime) {
if (
rows.slice(1, this.config.maxRowsToParse).every((v: any, i: any) => {
- const cellId = XLSX.utils.encode_cell({
+ const cellId = this.xlsx.utils.encode_cell({
c: range.s.c + col,
r: i + columnNameRowExist,
})
@@ -209,7 +229,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
if (table.columns[i].uidt === UITypes.Checkbox) {
rowData[table.columns[i].column_name] = getCheckboxValue(row[i])
} else if (table.columns[i].uidt === UITypes.Currency) {
- const cellId = XLSX.utils.encode_cell({
+ const cellId = this.xlsx.utils.encode_cell({
c: range.s.c + i,
r: rowIndex + columnNameRowExist,
})
diff --git a/packages/nc-gui/utils/validation.ts b/packages/nc-gui/utils/validation.ts
index 3b0a9b893b..be0571b5fd 100644
--- a/packages/nc-gui/utils/validation.ts
+++ b/packages/nc-gui/utils/validation.ts
@@ -80,18 +80,20 @@ export function validateColumnName(v: string, isGQL = false) {
}
export const projectTitleValidator = {
- validator: (rule: any, value: any, callback: (errMsg?: string) => void) => {
+ validator: (rule: any, value: any) => {
const { t } = getI18n().global
- if (value?.length > 50) {
- // callback('Project name exceeds 50 characters')
- callback(t('msg.error.projectNameExceeds50Characters'))
- }
- if (value[0] === ' ') {
- // callback('Project name cannot start with space')
- callback(t('msg.error.projectNameCannotStartWithSpace'))
- }
- callback()
+ return new Promise((resolve, reject) => {
+ if (value?.length > 50) {
+ reject(new Error(t('msg.error.projectNameExceeds50Characters')))
+ }
+
+ if (value[0] === ' ') {
+ reject(new Error(t('msg.error.projectNameCannotStartWithSpace')))
+ }
+
+ resolve(true)
+ })
},
}
diff --git a/packages/nocodb-sdk/package-lock.json b/packages/nocodb-sdk/package-lock.json
index 4e7656e0e1..407893fd5b 100644
--- a/packages/nocodb-sdk/package-lock.json
+++ b/packages/nocodb-sdk/package-lock.json
@@ -15806,4 +15806,4 @@
"dev": true
}
}
-}
\ No newline at end of file
+}
diff --git a/scripts/cypress/integration/common/00_pre_configurations.js b/scripts/cypress/integration/common/00_pre_configurations.js
index a44270d816..c4edc6c659 100644
--- a/scripts/cypress/integration/common/00_pre_configurations.js
+++ b/scripts/cypress/integration/common/00_pre_configurations.js
@@ -169,7 +169,7 @@ export const genTest = (apiType, dbType) => {
function cy_createProjectBlock(proj, apiType, dbType) {
// click home button
- cy.get(".nc-noco-brand-icon").click();
+ cy.getSettled(".nc-noco-brand-icon").click();
cy.get(".ant-table-content").then((obj) => {
// if project already created, open
// else, create a new one
@@ -240,7 +240,13 @@ export const genTest = (apiType, dbType) => {
}
// close team & auth tab
- cy.get("button.ant-tabs-tab-remove").should("exist").click();
+
+ // wait for tab to be rendered completely
+ cy.wait(2000);
+
+ cy.getSettled("button.ant-tabs-tab-remove")
+ .should("be.visible")
+ .click();
cy.get("button.ant-tabs-tab-remove").should("not.exist");
// first instance of updating local storage information
diff --git a/scripts/cypress/integration/common/1b_table_column_operations.js b/scripts/cypress/integration/common/1b_table_column_operations.js
index 75925ba475..f1d53f0895 100644
--- a/scripts/cypress/integration/common/1b_table_column_operations.js
+++ b/scripts/cypress/integration/common/1b_table_column_operations.js
@@ -10,6 +10,9 @@ export const genTest = (apiType, dbType) => {
function addNewRow(index, cellValue) {
cy.get(".nc-add-new-row-btn:visible").should("exist");
cy.get(".nc-add-new-row-btn").click();
+
+ cy.wait(2000);
+
// cy.get("#data-table-form-Title > input").first().type(cellValue);
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
@@ -143,6 +146,9 @@ export const genTest = (apiType, dbType) => {
.trigger("mouseover", { force: true });
cy.get(".nc-row-expand").click({ force: true });
+ // wait for page render to complete
+ cy.get('button:contains("Save row"):visible').should("exist");
+
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
.should("exist")
diff --git a/scripts/cypress/integration/common/3f_link_to_another_record.js b/scripts/cypress/integration/common/3f_link_to_another_record.js
index 3891fd5404..8e072ee185 100644
--- a/scripts/cypress/integration/common/3f_link_to_another_record.js
+++ b/scripts/cypress/integration/common/3f_link_to_another_record.js
@@ -5,6 +5,10 @@ import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
+ // tbd: this needs a proper fix
+ let waitTime = 2000;
+ let clear;
+
describe(`${apiType.toUpperCase()} api - Link to another record`, () => {
function fetchParentFromLabel(label) {
cy.get("label").contains(label).parents(".ant-row").click();
@@ -115,10 +119,12 @@ export const genTest = (apiType, dbType) => {
cy.wait("@unlinkCount");
}
- // before(() => {
- // // required for standalone test
- // // loginPage.loginAndOpenProject(apiType, dbType);
- // });
+ before(() => {
+ cy.restoreLocalStorage();
+
+ clear = Cypress.LocalStorage.clear;
+ Cypress.LocalStorage.clear = () => {};
+ });
afterEach(() => {
cy.saveLocalStorage();
@@ -141,6 +147,8 @@ export const genTest = (apiType, dbType) => {
cy.deleteTable("Sheet2");
cy.saveLocalStorage();
+
+ Cypress.LocalStorage.clear = clear;
});
///////////////////////////////////////////////////
@@ -169,11 +177,15 @@ export const genTest = (apiType, dbType) => {
// Expand form [Add new row]
//
it("Add HM, BT, MM Link, Expand form", () => {
+ // http://localhost:8080/api/v1/db/data/noco/p_0l53e1xgsxlecb/md_mn4xgb2jnq16a7?limit=10&offset=0&where=&fields[]=Title&fields[]=Id
+ cy.intercept("GET", `/api/v1/db/data/noco/**`).as("waitForCardLoad");
+
cy.openTableTab("Sheet2", 0);
// Click on `Add new row` button
cy.get(".nc-add-new-row-btn:visible").should("exist");
cy.get(".nc-add-new-row-btn").click();
+ cy.wait(waitTime);
// Title
cy.get(".nc-expand-col-Title")
@@ -193,26 +205,32 @@ export const genTest = (apiType, dbType) => {
.find(".nc-action-icon")
.should("exist")
.click({ force: true });
- cy.wait(1000);
+ cy.wait(waitTime);
+ cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(0)
.click();
+ cy.wait(waitTime);
// MM
cy.get(".nc-expand-col-Sheet1.List").find(".ant-btn-primary").click();
- cy.wait(1000);
+ cy.wait(waitTime);
+ cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(0)
.click();
+ cy.wait(waitTime);
// HM
cy.get(".nc-expand-col-Link2-1hm").find(".ant-btn-primary").click();
- cy.wait(1000);
+ cy.wait(waitTime);
+ cy.wait("@waitForCardLoad");
cy.getActiveModal().find(".ant-card").should("exist").eq(0).click();
+ cy.wait(waitTime);
// Save row
cy.getActiveDrawer(".nc-drawer-expanded-form")
@@ -238,12 +256,13 @@ export const genTest = (apiType, dbType) => {
.getCell("Sheet1", 2)
.find(".nc-action-icon")
.click({ force: true });
+ cy.wait(waitTime);
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
- cy.wait(1000);
+ cy.wait(waitTime);
// MM
mainPage
@@ -251,12 +270,13 @@ export const genTest = (apiType, dbType) => {
.find(".nc-action-icon")
.last()
.click({ force: true });
+ cy.wait(waitTime);
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
- cy.wait(1000);
+ cy.wait(waitTime);
// HM
mainPage
@@ -264,50 +284,65 @@ export const genTest = (apiType, dbType) => {
.find(".nc-action-icon")
.last()
.click({ force: true });
+ cy.wait(waitTime);
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
+ cy.wait(waitTime);
});
// Existing row, expand record
it("Add HM, BT, MM Link, expand record", () => {
+ // http://localhost:8080/api/v1/db/data/noco/p_0l53e1xgsxlecb/md_mn4xgb2jnq16a7?limit=10&offset=0&where=&fields[]=Title&fields[]=Id
+ cy.intercept("GET", `/api/v1/db/data/noco/**`).as("waitForCardLoad");
+
addRow(3, "2c");
+
+ cy.wait(waitTime);
cy.get(".nc-row-expand").eq(2).click({ force: true });
+ cy.wait(waitTime);
+
+ // wait for page render to complete
+ cy.get('button:contains("Save row"):visible').should("exist");
// BT
- cy.wait(1000);
+ cy.wait(waitTime);
cy.get(".nc-expand-col-Sheet1")
.find(".nc-action-icon")
.should("exist")
.click({ force: true });
- cy.wait(1000);
+ cy.wait(waitTime);
+ // cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
+ cy.wait(waitTime);
// MM
cy.get(".nc-expand-col-Sheet1.List").find(".ant-btn-primary").click();
- cy.wait(1000);
+ cy.wait(waitTime);
+ // cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
- cy.wait(1000);
+ cy.wait(waitTime);
// HM
cy.get(".nc-expand-col-Link2-1hm").find(".ant-btn-primary").click();
- cy.wait(1000);
+ cy.wait(waitTime);
+ // cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
- cy.wait(1000);
+ cy.wait(waitTime);
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
diff --git a/scripts/cypress/integration/common/4e_form_view_share.js b/scripts/cypress/integration/common/4e_form_view_share.js
index d976cee30b..9d3c5ceb34 100644
--- a/scripts/cypress/integration/common/4e_form_view_share.js
+++ b/scripts/cypress/integration/common/4e_form_view_share.js
@@ -62,6 +62,9 @@ export const genTest = (apiType, dbType) => {
// enable "Submit another form" check box
cy.get("button.nc-form-checkbox-show-blank-form").click();
+ // [kludge] CI-CD: title is being rendered initially in disabled state
+ cy.wait(2000);
+
// Update header & add some description, verify
cy.get(".nc-form")
.find('[placeholder="Form Title"]')
diff --git a/scripts/cypress/integration/common/4f_grid_view_share.js b/scripts/cypress/integration/common/4f_grid_view_share.js
index b815200d68..0fadfd5ee7 100644
--- a/scripts/cypress/integration/common/4f_grid_view_share.js
+++ b/scripts/cypress/integration/common/4f_grid_view_share.js
@@ -39,12 +39,17 @@ export const genTest = (apiType, dbType) => {
cy.getActiveModal(".nc-modal-share-view").should("not.be.visible");
};
+ let clear;
+
describe(`${apiType.toUpperCase()} api - GRID view (Share)`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
cy.openTableTab("Address", 25);
+
+ clear = Cypress.LocalStorage.clear;
+ Cypress.LocalStorage.clear = () => {};
});
beforeEach(() => {
@@ -61,6 +66,8 @@ export const genTest = (apiType, dbType) => {
cy.restoreLocalStorage();
cy.closeTableTab("Address");
cy.saveLocalStorage();
+
+ Cypress.LocalStorage.clear = clear;
});
// Common routine to create/edit/delete GRID & GALLERY view
diff --git a/scripts/cypress/integration/common/4g_table_view_expanded_form.js b/scripts/cypress/integration/common/4g_table_view_expanded_form.js
index 1094a7e4df..3031a30654 100644
--- a/scripts/cypress/integration/common/4g_table_view_expanded_form.js
+++ b/scripts/cypress/integration/common/4g_table_view_expanded_form.js
@@ -31,10 +31,10 @@ export const genTest = (apiType, dbType) => {
// open a table to work on views
//
- cy.openTableTab('Country', 25);
+ cy.openTableTab("Country", 25);
clear = Cypress.LocalStorage.clear;
- Cypress.LocalStorage.clear = () => {}
+ Cypress.LocalStorage.clear = () => {};
});
beforeEach(() => {
@@ -91,6 +91,9 @@ export const genTest = (apiType, dbType) => {
// click on first row-expand if grid & first card if its gallery
if (viewType === "grid") {
cy.get(".nc-row-expand").first().click({ force: true });
+
+ // wait for page render to complete
+ cy.get('button:contains("Save row"):visible').should("exist");
} else if (viewType === "gallery") {
cy.get(".nc-gallery-container .ant-card").first().click();
}
diff --git a/scripts/cypress/integration/common/5a_user_role.js b/scripts/cypress/integration/common/5a_user_role.js
index 8ccba12e06..e52eadaf66 100644
--- a/scripts/cypress/integration/common/5a_user_role.js
+++ b/scripts/cypress/integration/common/5a_user_role.js
@@ -147,8 +147,11 @@ export const genTest = (apiType, dbType) => {
if (roleType === "creator") {
// kludge: wait for page load to finish
// close team & auth tab
- cy.wait(500);
- cy.get("button.ant-tabs-tab-remove").should("exist").click();
+ // cy.wait(500);
+ // cy.get("button.ant-tabs-tab-remove").should("exist").click();
+ cy.getSettled("button.ant-tabs-tab-remove")
+ .should("be.visible")
+ .click();
cy.wait(500);
}
diff --git a/scripts/cypress/integration/common/8a_webhook.js b/scripts/cypress/integration/common/8a_webhook.js
index f39b8a6935..4f9e633fc7 100644
--- a/scripts/cypress/integration/common/8a_webhook.js
+++ b/scripts/cypress/integration/common/8a_webhook.js
@@ -16,24 +16,13 @@ function createWebhook(hook, test) {
cy.get(".nc-btn-create-webhook").should("exist").click();
// hardcode "Content-type: application/json"
- cy.get(".ant-tabs-tab-btn").contains("Headers").should("exist").click();
-
- // kludge : as neither scrollIntoView nor scrollTo didn't yield any results
- // cy.getActiveSelection().find('.ant-select-item').contains('Content-Type).scrollIntoView();
- // cy.getActiveSelection().find('.rc-virtual-list').scrollTo('center');
- // cy.getActiveSelection().select('Content-Type', { force: true });
+ cy.get(`.ant-tabs-tab-btn:contains("Headers")`).should("exist").click();
cy.get(".nc-input-hook-header-key")
.should("exist")
.click()
.type("Content-Type{enter}");
- // cy.getActiveSelection(".nc-dropdown-webhook-header")
- // .find(".ant-select-item-option-content")
- // .contains("Content-Type")
- // .should("exist")
- // .click();
-
cy.get("input.nc-input-hook-header-value")
.should("exist")
.clear({ force: true })
@@ -184,6 +173,9 @@ function updateRow(index, cellValue) {
.eq(index - 1)
.click({ force: true });
+ // wait for page render to complete
+ cy.get('button:contains("Save row"):visible').should("exist");
+
cy.get(".nc-expand-col-Title")
.should("exist")
.find(".nc-cell > input")
diff --git a/scripts/cypress/integration/common/9a_QuickTest.js b/scripts/cypress/integration/common/9a_QuickTest.js
index 24481a2604..f808667d0e 100644
--- a/scripts/cypress/integration/common/9a_QuickTest.js
+++ b/scripts/cypress/integration/common/9a_QuickTest.js
@@ -56,13 +56,18 @@ let cn = [
];
function openWebhook(index) {
+ // http://localhost:8080/api/v1/db/meta/tables/md_dx81kkfdso115u/hooks
+ cy.intercept("GET", "/api/v1/db/meta/tables/*/hooks").as("getHooks");
+
cy.get(".nc-actions-menu-btn").should("exist").click();
cy.getActiveMenu(".nc-dropdown-actions-menu")
.find(".ant-dropdown-menu-title-content")
.contains("Webhooks")
.click();
- cy.get(".nc-hook").eq(index).click();
+ cy.wait("@getHooks");
+
+ cy.get(`.nc-hook:eq(${index})`).should("exist").click();
}
// to be invoked after open
diff --git a/scripts/cypress/integration/common/9b_ERD.js b/scripts/cypress/integration/common/9b_ERD.js
index 601ec9b202..0bff55311d 100644
--- a/scripts/cypress/integration/common/9b_ERD.js
+++ b/scripts/cypress/integration/common/9b_ERD.js
@@ -1,4 +1,4 @@
-import { mainPage } from "../../support/page_objects/mainPage";
+import { mainPage, settingsPage } from "../../support/page_objects/mainPage";
import {loginPage} from "../../support/page_objects/navigation";
import { isTestSuiteActive, mysqlSakilaSqlViews, mysqlSakilaTables, pgSakilaSqlViews, pgSakilaTables, sqliteSakilaSqlViews } from "../../support/page_objects/projectConstants";
@@ -59,6 +59,11 @@ export const genTest = (apiType, dbType) => {
it(`Verify ERD Context menu in all table view`, () => {
mainPage.openErdTab();
+ cy.wait(2000)
+ // todo: Edges are not rendering properly in cypress in first render
+ settingsPage.openTab(settingsPage.UI_ACCESS_CONTROL)
+ settingsPage.openTab(settingsPage.ERD)
+
cy.get('.nc-erd-context-menu').should('be.visible');
cy.get('.nc-erd-context-menu').get('.nc-erd-histogram').should('be.visible');
cy.get('.nc-erd-context-menu').find('.ant-checkbox').should('have.length', 3);
@@ -68,6 +73,10 @@ export const genTest = (apiType, dbType) => {
cy.get('.nc-erd-context-menu').find('.nc-erd-showColumns-label').dblclick();
cy.get('.nc-erd-context-menu').find('.ant-checkbox').should('have.length', 5);
+
+ // todo: Enabling and disabling showJunctionTableNames rerenders `mm` edges since `mm` edges is not getting rendered in cypress
+ cy.get('.nc-erd-context-menu').get('.nc-erd-showJunctionTableNames-checkbox').click();
+ cy.get('.nc-erd-context-menu').get('.nc-erd-showJunctionTableNames-checkbox').click();
});
it("Verify ERD of all tables view and verify columns of actor and payment with default config", () => {
@@ -311,6 +320,10 @@ export const genTest = (apiType, dbType) => {
cy.get('.nc-erd-context-menu').get('.nc-erd-showViews-checkbox').click();
cy.get('.nc-erd-context-menu').get('.nc-erd-showMMTables-checkbox').click();
+ // Enabling and disabling showJunctionTableNames rerenders `mm` edges since `mm` edges is not getting rendered in cypress
+ cy.get('.nc-erd-context-menu').get('.nc-erd-showJunctionTableNames-checkbox').click();
+ cy.get('.nc-erd-context-menu').get('.nc-erd-showJunctionTableNames-checkbox').click();
+
if(dbType === "mysql") {
cy.get('.nc-erd-vue-flow').find('.nc-erd-table-node').should('have.length', 16)
cy.get('.nc-erd-vue-flow').find('.vue-flow__edge').should('have.length', 26)
diff --git a/scripts/cypress/integration/spec/roleValidation.spec.js b/scripts/cypress/integration/spec/roleValidation.spec.js
index 6d08ac9273..fc03f211e2 100644
--- a/scripts/cypress/integration/spec/roleValidation.spec.js
+++ b/scripts/cypress/integration/spec/roleValidation.spec.js
@@ -162,6 +162,10 @@ export function _editData(roleType, mode) {
.eq(0)
.trigger("mouseover", { force: true });
cy.get(".nc-row-expand").should("exist").eq(10).click({ force: true });
+
+ // wait for page render to complete
+ cy.get('button:contains("Save row"):visible').should("exist");
+
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
@@ -175,6 +179,10 @@ export function _editData(roleType, mode) {
// update cell contents option using row expander should be disabled
//
cy.get(".nc-row-expand").should("exist").eq(10).click({ force: true });
+
+ // wait for page render to complete
+ cy.get('button:contains("Save row"):visible').should("exist");
+
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button:disabled")
.contains("Save row")
diff --git a/scripts/cypress/support/commands.js b/scripts/cypress/support/commands.js
index 26a50b58d0..303531a2a5 100644
--- a/scripts/cypress/support/commands.js
+++ b/scripts/cypress/support/commands.js
@@ -29,6 +29,36 @@ import { isXcdb, isPostgres } from "./page_objects/projectConstants";
require("@4tw/cypress-drag-drop");
+// recursively gets an element, returning only after it's determined to be attached to the DOM for good
+Cypress.Commands.add('getSettled', (selector, opts = {}) => {
+ const retries = opts.retries || 3;
+ const delay = opts.delay || 400;
+
+ const isAttached = (resolve, count = 0) => {
+ const el = Cypress.$(selector);
+
+ // is element attached to the DOM?
+ count = Cypress.dom.isAttached(el) ? count + 1 : 0;
+
+ // hit our base case, return the element
+ if (count >= retries) {
+ return resolve(el);
+ }
+
+ // retry after a bit of a delay
+ setTimeout(() => isAttached(resolve, count), delay);
+ };
+
+ // wrap, so we can chain cypress commands off the result
+ return cy.wrap(null).then(() => {
+ return new Cypress.Promise((resolve) => {
+ return isAttached(resolve, 0);
+ }).then((el) => {
+ return cy.wrap(el);
+ });
+ });
+});
+
// for waiting until page load
Cypress.Commands.add("waitForSpinners", () => {
cy.visit("http://localhost:3000/signup", {
@@ -252,9 +282,9 @@ Cypress.Commands.add("getActiveModal", (wrapperSelector) => {
Cypress.Commands.add("getActiveMenu", (overlaySelector) => {
if (overlaySelector) {
- return cy.get(`${overlaySelector} .ant-dropdown-content:visible`);
+ return cy.getSettled(`${overlaySelector} .ant-dropdown-content:visible`);
}
- return cy.get(".ant-dropdown-content:visible").last();
+ return cy.getSettled(".ant-dropdown-content:visible").last();
});
Cypress.Commands.add("getActivePopUp", () => {
diff --git a/scripts/cypress/support/page_objects/mainPage.js b/scripts/cypress/support/page_objects/mainPage.js
index 3d2c2b9e58..1b368e30ee 100644
--- a/scripts/cypress/support/page_objects/mainPage.js
+++ b/scripts/cypress/support/page_objects/mainPage.js
@@ -204,11 +204,11 @@ export class _mainPage {
};
addColumn = (colName, tableName) => {
- cy.get(".nc-column-add").click({
- force: true,
- });
+ cy.get(".nc-column-add").click();
- cy.getActiveMenu(".nc-dropdown-grid-add-column")
+ cy.wait(2000);
+
+ cy.getActiveMenu(".nc-dropdown-grid-add-column:has(.nc-column-name-input)")
.find("input.nc-column-name-input")
.should("exist")
.clear()
@@ -219,22 +219,24 @@ export class _mainPage {
.should("exist")
.click();
cy.toastWait(`Column created`);
+
+ cy.wait(2000);
+
cy.get(`th[data-title="${colName}"]`).should("exist");
};
addColumnWithType = (colName, colType, tableName) => {
- cy.get(".nc-column-add").click({
- force: true,
- });
+ cy.get(".nc-column-add").click();
- cy.getActiveMenu(".nc-dropdown-grid-add-column")
+ cy.wait(2000);
+
+ cy.getActiveMenu(".nc-dropdown-grid-add-column:has(.nc-column-name-input)")
.find("input.nc-column-name-input")
.should("exist")
.clear()
.type(colName);
// change column type and verify
- // cy.get(".nc-column-type-input").last().click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".nc-column-type-input")
.last()
@@ -243,13 +245,15 @@ export class _mainPage {
.find(".ant-select-item-option")
.contains(colType)
.click();
- // cy.get(".ant-btn-primary:visible").contains("Save").click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait(`Column created`);
+
+ cy.wait(2000);
+
cy.get(`th[data-title="${colName}"]`).should("exist");
};
@@ -396,7 +400,7 @@ export class _mainPage {
.find(".ant-btn-primary")
.contains("Add Sort Option")
.click();
- cy.getActiveMenu(".nc-dropdown-sort-menu")
+ cy.getActiveMenu(".nc-dropdown-sort-menu:has(.nc-sort-field-select div)")
.find(".nc-sort-field-select div")
.first()
.click();
@@ -434,7 +438,8 @@ export class _mainPage {
.find(".ant-btn-primary")
.contains("Add Filter")
.click();
- cy.getActiveMenu(".nc-dropdown-filter-menu")
+
+ cy.getActiveMenu(".nc-dropdown-filter-menu:has(.nc-filter-field-select)")
.find(".nc-filter-field-select")
.should("exist")
.last()
@@ -488,11 +493,10 @@ export class _mainPage {
// one of the row would contain seggregation header ('other views)
if (5 == $tableRow[0].childElementCount) {
cy.wrap($tableRow).find(".nc-icon").last().click();
- cy.wait(100);
+ cy.toastWait("Deleted shared view successfully");
}
})
.then(() => {
- cy.toastWait("Deleted shared view successfully");
cy.getActiveModal()
.find("button.ant-modal-close")
.should("exist")
diff --git a/scripts/cypress/support/page_objects/navigation.js b/scripts/cypress/support/page_objects/navigation.js
index 050c7aaef8..20f664c7ee 100644
--- a/scripts/cypress/support/page_objects/navigation.js
+++ b/scripts/cypress/support/page_objects/navigation.js
@@ -108,7 +108,7 @@ export class _projectsPage {
cy.wait("@waitForPageLoad");
// close team & auth tab
- cy.get("button.ant-tabs-tab-remove").should("exist").click();
+ cy.getSettled("button.ant-tabs-tab-remove").should("be.visible").click();
cy.get("button.ant-tabs-tab-remove").should("not.exist");
}
|