+
{{ $t('general.cancel') }}
diff --git a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue
index 9509144597..2be468ce4a 100644
--- a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue
+++ b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue
@@ -229,7 +229,6 @@ const isTableOpened = computed(() => {
{{ table.title }}
-
- {{ $t('activity.metaSync') }}
+ {{ $t('tooltip.metaSync') }}
void
@@ -114,6 +115,7 @@ const tabsInfo: TabGroup = {
subTabs: {
misc: {
// Misc
+ key: 'Misc',
title: t('general.misc'),
body: Misc,
},
@@ -254,7 +256,7 @@ watch(
v-model:state="vDataState"
v-model:reload="dataSourcesReload"
class="px-2 pb-2"
- :data-testid="`nc-settings-subtab-${selectedSubTab.title}`"
+ :data-testid="`nc-settings-subtab-${selectedSubTab.key}`"
:project-id="projectId"
@awaken="handleAwaken"
/>
@@ -263,7 +265,7 @@ watch(
v-else
class="px-2 py-6"
:project-id="projectId"
- :data-testid="`nc-settings-subtab-${selectedSubTab.title}`"
+ :data-testid="`nc-settings-subtab-${selectedSubTab.key}`"
/>
diff --git a/packages/nc-gui/components/monaco/Editor.vue b/packages/nc-gui/components/monaco/Editor.vue
index 442531ed5a..4dbfb01049 100644
--- a/packages/nc-gui/components/monaco/Editor.vue
+++ b/packages/nc-gui/components/monaco/Editor.vue
@@ -90,8 +90,8 @@ onMounted(async () => {
foldingStrategy: 'indentation',
selectOnLineNumbers: true,
scrollbar: {
- verticalScrollbarSize: 8,
- horizontalScrollbarSize: 8,
+ verticalScrollbarSize: 1,
+ horizontalScrollbarSize: 1,
},
tabSize: 2,
automaticLayout: true,
diff --git a/packages/nc-gui/components/smartsheet/Details.vue b/packages/nc-gui/components/smartsheet/Details.vue
index ef0ef3d337..7b6975c953 100644
--- a/packages/nc-gui/components/smartsheet/Details.vue
+++ b/packages/nc-gui/components/smartsheet/Details.vue
@@ -16,6 +16,9 @@ const openedSubTab = computed({
watch(
openedSubTab,
() => {
+ if (openedSubTab.value === 'field' && !isUIAllowed('hookList')) {
+ onViewsTabChange('relation')
+ }
if (openedSubTab.value === 'webhook' && !isUIAllowed('hookList')) {
onViewsTabChange('relation')
}
@@ -28,7 +31,16 @@ watch(
-
+
+
+
+
+
+
+
@@ -67,6 +79,14 @@ watch(
@apply flex flex-row items-center gap-x-1.5 pr-0.5;
}
+:deep(.nc-tabs.centered) {
+ > .ant-tabs-nav {
+ .ant-tabs-nav-wrap {
+ @apply absolute mx-auto -left-1/8 right-0;
+ }
+ }
+}
+
:deep(.ant-tabs-nav) {
min-height: calc(var(--topbar-height) - 1.75px);
}
diff --git a/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue b/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
index 1b60419687..764793c103 100644
--- a/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
+++ b/packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
@@ -56,6 +56,8 @@ const { appInfo } = useGlobal()
const { betaFeatureToggleState } = useBetaFeatureToggle()
+const { openedViewsTab } = storeToRefs(useViewsStore())
+
const { loadMagic, predictColumnType: _predictColumnType } = useNocoEe()
const meta = inject(MetaInj, ref())
@@ -194,6 +196,10 @@ const handleEscape = (event: KeyboardEvent): void => {
if (event.key === 'Escape') emit('cancel')
}
+const isFieldsTab = computed(() => {
+ return openedViewsTab.value === 'field'
+})
+
if (props.fromTableExplorer) {
watch(
formState,
@@ -220,8 +226,21 @@ if (props.fromTableExplorer) {
>
+
+
+
+
+
+import { diff } from 'deep-object-diff'
+import { message } from 'ant-design-vue'
+import { UITypes, isSystemColumn } from 'nocodb-sdk'
+import Draggable from 'vuedraggable'
+import type { ColumnType, SelectOptionsType, TableType } from 'nocodb-sdk'
+import { Icon } from '@iconify/vue'
+import { type Field, getUniqueColumnName, ref, useSmartsheetStoreOrThrow } from '#imports'
+
+interface TableExplorerColumn extends ColumnType {
+ id?: string
+ temp_id?: string
+ column_order?: {
+ order: number
+ view_id: string
+ }
+}
+
+interface op {
+ op: 'add' | 'update' | 'delete'
+ column: TableExplorerColumn
+}
+
+interface fieldsVisibilityOps {
+ visible: boolean
+ column: TableExplorerColumn
+}
+
+interface moveOp {
+ op: 'move'
+ column: TableExplorerColumn
+ index: number
+ order: number
+}
+
+const { $api } = useNuxtApp()
+
+const { getMeta } = useMetas()
+
+const { meta, view } = useSmartsheetStoreOrThrow()
+
+const moveOps = ref([])
+
+const visibilityOps = ref([])
+
+const selectedView = inject(ActiveViewInj)
+
+const { fields: viewFields, toggleFieldVisibility, loadViewColumns } = useViewColumns(view, meta as Ref)
+
+const loading = ref(false)
+
+const columnsHash = ref()
+
+const newFields = ref([])
+
+const compareCols = (a?: TableExplorerColumn, b?: TableExplorerColumn) => {
+ if (a?.id && b?.id) {
+ return a.id === b.id
+ } else if (a?.temp_id && b?.temp_id) {
+ return a.temp_id === b.temp_id
+ }
+ return false
+}
+
+const viewFieldsMap = computed>(() => {
+ const temp: Record = {}
+ if (viewFields.value) {
+ for (const field of viewFields.value) {
+ if (field.fk_column_id) temp[field.fk_column_id] = field
+ }
+ }
+ return temp
+})
+
+const getFieldOrder = (field?: TableExplorerColumn) => {
+ if (!field) return -1
+ const mop = moveOps.value.find((op) => compareCols(op.column, field))
+ if (mop) {
+ return mop.order
+ } else if (field.id) {
+ const viewField = viewFieldsMap.value[field.id]
+ if (viewField) {
+ return viewField.order
+ }
+ }
+ return -1
+}
+
+const fields = computed({
+ get: () => {
+ const x = (meta.value?.columns as ColumnType[])
+ .filter((field) => !field.fk_column_id && !isSystemColumn(field))
+ .concat(newFields.value)
+ .sort((a, b) => {
+ return getFieldOrder(a) - getFieldOrder(b)
+ })
+ return x
+ },
+ set: (val) => {
+ meta.value!.columns = meta.value?.columns?.map((col) => {
+ const field = val.find((f) => compareCols(f, col))
+ if (field) {
+ return field
+ }
+ return col
+ })
+ },
+})
+
+// Current Selected Field
+const activeField = ref()
+
+const searchQuery = ref('')
+
+const calculateOrderForIndex = (index: number, fromAbove = false) => {
+ if (!viewFields.value) return -1
+
+ if (index <= 0) {
+ const pv = fields.value.find((f) => f.pv)
+ if (pv) {
+ return pv.order || 0
+ }
+ return -1
+ }
+
+ if (index >= fields.value.length - 1) {
+ const fieldOrders = fields.value.map((f) => getFieldOrder(f))
+ return Math.max(...fieldOrders) + 1
+ }
+
+ let orderBefore = -1
+ let orderAfter = -1
+
+ const fieldBefore = fields.value[index + (fromAbove ? -1 : 0)]
+ const fieldAfter = fields.value[index + (fromAbove ? 0 : 1)]
+
+ if (fieldBefore) {
+ orderBefore = getFieldOrder(fieldBefore)
+ }
+
+ if (fieldAfter) {
+ orderAfter = getFieldOrder(fieldAfter)
+ if (orderAfter === -1) {
+ orderAfter = orderBefore + 1
+ }
+ }
+
+ const order = (orderBefore + orderAfter) / 2
+
+ return order
+}
+
+// Update, Delete and New Column operations are tracked here
+const ops = ref([])
+
+const temporaryAddCount = ref(0)
+
+const changingField = ref(false)
+
+const addFieldMoveHook = ref()
+
+const duplicateFieldHook = ref()
+
+const setFieldMoveHook = (field: TableExplorerColumn, before = false) => {
+ const index = fields.value.findIndex((f) => compareCols(f, field))
+ if (index !== -1) {
+ addFieldMoveHook.value = before ? index : index + 1
+ }
+}
+
+const changeField = (field?: TableExplorerColumn, event?: MouseEvent) => {
+ if (event) {
+ if (event.target instanceof HTMLElement) {
+ if (event.target.closest('.no-action')) return
+ }
+ }
+
+ if (compareCols(field, activeField.value) || (field === undefined && activeField.value === undefined)) return
+
+ changingField.value = true
+ nextTick(() => {
+ activeField.value = field
+ changingField.value = false
+ })
+}
+
+const addField = (field?: TableExplorerColumn, before = false) => {
+ if (field) {
+ setFieldMoveHook(field, before)
+ }
+ changeField({})
+}
+
+const displayColumn = computed(() => {
+ if (!meta.value?.columns) return
+ return meta.value?.columns.find((col) => col.pv)
+})
+
+const duplicateField = async (field: TableExplorerColumn) => {
+ if (!meta.value?.columns) return
+
+ // generate duplicate column name
+ const duplicateColumnName = getUniqueColumnName(`${field.title}_copy`, meta.value?.columns)
+
+ let fieldPayload = {}
+
+ // construct column create payload
+ switch (field.uidt) {
+ case UITypes.LinkToAnotherRecord:
+ case UITypes.Links:
+ case UITypes.Lookup:
+ case UITypes.Rollup:
+ case UITypes.Formula:
+ return message.info('Not available at the moment')
+ case UITypes.SingleSelect:
+ case UITypes.MultiSelect:
+ fieldPayload = {
+ ...field,
+ title: duplicateColumnName,
+ column_name: duplicateColumnName,
+ id: undefined,
+ order: undefined,
+ pv: false,
+ colOptions: {
+ options:
+ (field.colOptions as SelectOptionsType)?.options?.map((option: Record) => ({
+ ...option,
+ id: undefined,
+ })) ?? [],
+ },
+ }
+ break
+ default:
+ fieldPayload = {
+ ...field,
+ title: duplicateColumnName,
+ column_name: duplicateColumnName,
+ id: undefined,
+ colOptions: undefined,
+ order: undefined,
+ pv: false,
+ }
+ break
+ }
+
+ addField(field)
+
+ duplicateFieldHook.value = fieldPayload as TableExplorerColumn
+}
+
+// This method is called whenever there is a change in field properties
+const onFieldUpdate = (state: TableExplorerColumn) => {
+ const col = fields.value.find((col) => compareCols(col, state))
+ if (!col) return
+
+ const diffs = diff(col, state)
+ if (Object.keys(diffs).length === 0 || (Object.keys(diffs).length === 1 && 'altered' in diffs)) {
+ ops.value = ops.value.filter((op) => op.op === 'add' || !compareCols(op.column, state))
+ } else {
+ const field = ops.value.find((op) => compareCols(op.column, state))
+ if (field) {
+ field.column = state
+ } else {
+ ops.value.push({
+ op: 'update',
+ column: state,
+ })
+ }
+ }
+}
+
+const onFieldDelete = (state: TableExplorerColumn) => {
+ const field = ops.value.find((op) => compareCols(op.column, state))
+ if (field) {
+ if (field.op === 'delete') {
+ ops.value = ops.value.filter((op) => op.column.id !== state.id)
+ } else if (field.op === 'add') {
+ if (activeField.value && compareCols(activeField.value, state)) {
+ changeField()
+ }
+ ops.value = ops.value.filter((op) => op.column.temp_id !== state.temp_id)
+ newFields.value = newFields.value.filter((op) => op.temp_id !== state.temp_id)
+ } else {
+ field.op = 'delete'
+ field.column = state
+ }
+ } else {
+ ops.value.push({
+ op: 'delete',
+ column: state,
+ })
+ }
+}
+
+const onFieldAdd = (state: TableExplorerColumn) => {
+ if (duplicateFieldHook.value) {
+ state = duplicateFieldHook.value
+ duplicateFieldHook.value = undefined
+ }
+
+ state.temp_id = `temp_${++temporaryAddCount.value}`
+ ops.value.push({
+ op: 'add',
+ column: state,
+ })
+ newFields.value.push(state)
+
+ if (addFieldMoveHook.value) {
+ moveOps.value.push({
+ op: 'move',
+ column: state,
+ index: addFieldMoveHook.value,
+ order: calculateOrderForIndex(addFieldMoveHook.value),
+ })
+ addFieldMoveHook.value = undefined
+ } else {
+ moveOps.value.push({
+ op: 'move',
+ column: state,
+ index: fields.value.length,
+ order: calculateOrderForIndex(fields.value.length),
+ })
+ }
+
+ changeField(state)
+}
+
+const onMove = (_event: { moved: { newIndex: number; oldIndex: number } }) => {
+ const order = calculateOrderForIndex(_event.moved.newIndex, _event.moved.newIndex < _event.moved.oldIndex)
+
+ const field = fields.value[_event.moved.oldIndex]
+
+ const op = ops.value.find((op) => compareCols(op.column, field))
+
+ if (op) {
+ onFieldUpdate({
+ ...op.column,
+ column_order: {
+ order,
+ view_id: view.value?.id as string,
+ },
+ })
+ } else {
+ onFieldUpdate({
+ ...field,
+ column_order: {
+ order,
+ view_id: view.value?.id as string,
+ },
+ })
+ }
+
+ const mop = moveOps.value.find((op) => compareCols(op.column, fields.value[_event.moved.oldIndex]))
+ if (mop) {
+ mop.index = _event.moved.newIndex
+ mop.order = order
+ } else {
+ moveOps.value.push({
+ op: 'move',
+ column: fields.value[_event.moved.oldIndex],
+ index: _event.moved.newIndex,
+ order,
+ })
+ }
+}
+
+const recoverField = (state: TableExplorerColumn) => {
+ const field = ops.value.find((op) => compareCols(op.column, state))
+ if (field) {
+ if (field.op === 'delete') {
+ ops.value = ops.value.filter((op) => !compareCols(op.column, state))
+ } else if (field.op === 'update') {
+ ops.value = ops.value.filter((op) => !compareCols(op.column, state))
+ }
+ activeField.value = null
+ changeField(fields.value.filter((fiel) => fiel.id === state.id)[0])
+ }
+}
+
+const fieldState = (field: TableExplorerColumn) => {
+ const col = fields.value.find((col) => compareCols(col, field))
+ if (col) {
+ const op = ops.value.find((op) => compareCols(op.column, col))
+ if (op) {
+ return op.column
+ }
+ }
+ return null
+}
+
+const fieldStatuses = computed>(() => {
+ const statuses: Record = {}
+ for (const op of ops.value) {
+ if (op.op === 'add') {
+ if (op.column.temp_id) statuses[op.column.temp_id] = 'add'
+ } else if (op.op === 'update') {
+ if (op.column.id) statuses[op.column.id] = 'update'
+ } else if (op.op === 'delete') {
+ if (op.column.id) statuses[op.column.id] = 'delete'
+ }
+ }
+ return statuses
+})
+
+const fieldStatus = (field?: TableExplorerColumn) => {
+ const id = field?.id || field?.temp_id
+ return id ? fieldStatuses.value[id] : ''
+}
+
+const clearChanges = () => {
+ ops.value = []
+ moveOps.value = []
+ newFields.value = []
+ visibilityOps.value = []
+ changeField()
+}
+
+const saveChanges = async () => {
+ try {
+ if (!meta.value?.id) return
+
+ loading.value = true
+
+ for (const mop of moveOps.value) {
+ const op = ops.value.find((op) => compareCols(op.column, mop.column))
+ if (op && op.op === 'add') {
+ op.column.column_order = {
+ order: mop.order,
+ view_id: view.value?.id as string,
+ }
+ }
+ }
+
+ for (const f of fields.value) {
+ console.log(f.title, getFieldOrder(f))
+ }
+
+ for (const op of ops.value) {
+ if (op.op === 'add') {
+ if (activeField.value && compareCols(activeField.value, op.column)) {
+ changeField()
+ }
+ } else if (op.op === 'delete') {
+ if (activeField.value && compareCols(activeField.value, op.column)) {
+ changeField()
+ }
+ }
+ }
+
+ const res = await $api.dbTableColumn.bulk(meta.value?.id, {
+ hash: columnsHash.value,
+ ops: ops.value,
+ })
+
+ for (const op of visibilityOps.value) {
+ await toggleFieldVisibility(op.visible, {
+ ...op.column,
+ show: op.visible,
+ })
+ }
+ await loadViewColumns()
+
+ if (res) {
+ ops.value = (res.failedOps as op[]) || []
+ newFields.value = newFields.value.filter((col) => {
+ if (res.failedOps) {
+ const op = res.failedOps.find((fop) => {
+ return (fop.column as TableExplorerColumn).temp_id === col.temp_id
+ })
+ if (op) {
+ return true
+ }
+ }
+ return false
+ })
+ moveOps.value = []
+ }
+
+ await getMeta(meta.value.id, true)
+ columnsHash.value = (await $api.dbTableColumn.hash(meta.value?.id)).hash
+
+ visibilityOps.value = []
+ } catch (e) {
+ message.error('Something went wrong')
+ } finally {
+ loading.value = false
+ }
+}
+
+const toggleVisibility = async (checked: boolean, field: Field) => {
+ if (field.fk_column_id && fieldStatuses.value[field.fk_column_id]) {
+ message.warning('You cannot change visibility of a field that is being edited. Please save or discard changes first.')
+ return
+ }
+ if (visibilityOps.value.find((op) => op.column.fk_column_id === field.fk_column_id)) {
+ visibilityOps.value = visibilityOps.value.filter((op) => op.column.fk_column_id !== field.fk_column_id)
+ return
+ }
+ visibilityOps.value.push({
+ visible: checked,
+ column: field,
+ })
+}
+
+onMounted(async () => {
+ if (!meta.value?.id) return
+ columnsHash.value = (await $api.dbTableColumn.hash(meta.value?.id)).hash
+})
+
+
+
+
+
+
+
+
+
Fields
+
+
+
+
+
+
+
+
+ {{ selectedView?.title }}
+
+
+
+
+
+ Reset
+
+
+ Save changes
+
+
+
+
+
+
+
+
+
+
+
![](~assets/img/fieldPlaceholder.svg)
+
Select a field
+
+ Make changes to field properties by selecting a field from the list
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nc-gui/components/smartsheet/grid/Table.vue b/packages/nc-gui/components/smartsheet/grid/Table.vue
index 40140af122..5250127c6c 100644
--- a/packages/nc-gui/components/smartsheet/grid/Table.vue
+++ b/packages/nc-gui/components/smartsheet/grid/Table.vue
@@ -8,6 +8,7 @@ import {
FieldsInj,
IsGroupByInj,
IsLockedInj,
+ JsonExpandInj,
MetaInj,
NavigateDir,
ReadonlyInj,
@@ -225,6 +226,9 @@ const showContextMenu = (e: MouseEvent, target?: { row: number; col: number }) =
}
}
+const isJsonExpand = ref(false)
+provide(JsonExpandInj, isJsonExpand)
+
// #Cell - 1
async function clearCell(ctx: { row: number; col: number } | null, skipUpdate = false) {
@@ -1316,6 +1320,7 @@ const handleCellClick = (event: MouseEvent, row: number, col: number) => {
v-if="addColumnDropdown"
:preload="preloadColumn"
:column-position="columnOrder"
+ :class="{ hidden: isJsonExpand }"
@submit="closeAddColumnDropdown(true)"
@cancel="closeAddColumnDropdown()"
@click.stop
diff --git a/packages/nc-gui/components/smartsheet/grid/newFile.ts b/packages/nc-gui/components/smartsheet/grid/newFile.ts
new file mode 100644
index 0000000000..ceda4f5dc6
--- /dev/null
+++ b/packages/nc-gui/components/smartsheet/grid/newFile.ts
@@ -0,0 +1,4 @@
+import { JsonExpandInj, provide } from '#imports';
+import { isJsonExpand } from './Table.vue';
+
+provide(JsonExpandInj, isJsonExpand);
diff --git a/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue b/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue
index d6a6e0abc0..d69efe3ffb 100644
--- a/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue
+++ b/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue
@@ -24,7 +24,7 @@ const { onViewsTabChange } = useViewsStore()
:class="{
active: openedViewsTab !== 'view',
}"
- @click="onViewsTabChange(isEeUI ? 'field' : 'relation')"
+ @click="onViewsTabChange('field')"
>
) {
// Return a successful response back to the calling service
api.instance.interceptors.response.use(
- (response) => response,
+ (response) => {
+ return response
+ },
// Handle Error
(error) => {
if (error.response && error.response.data && error.response.data.msg === DbNotFoundMsg) return router.replace('/project/0')
diff --git a/packages/nc-gui/composables/useViewFilters.ts b/packages/nc-gui/composables/useViewFilters.ts
index 164a08b7b4..40c718d319 100644
--- a/packages/nc-gui/composables/useViewFilters.ts
+++ b/packages/nc-gui/composables/useViewFilters.ts
@@ -447,10 +447,6 @@ export function useViewFilters(
const lookupRelation = (await getMeta(nextCol.fk_model_id))?.columns?.find(
(c) => c.id === (nextCol.colOptions as LookupType).fk_relation_column_id,
)
- if ((lookupRelation.colOptions as LinkToAnotherRecordType).type !== RelationTypes.BELONGS_TO) {
- btLookup = false
- continue
- }
const relatedTableMeta = await getMeta((lookupRelation.colOptions as LinkToAnotherRecordType).fk_related_model_id)
nextCol = relatedTableMeta?.columns?.find((c) => c.id === (nextCol.colOptions as LookupType).fk_lookup_column_id)
diff --git a/packages/nc-gui/context/index.ts b/packages/nc-gui/context/index.ts
index 7fc4e8ddbd..81ebfeaf0e 100644
--- a/packages/nc-gui/context/index.ts
+++ b/packages/nc-gui/context/index.ts
@@ -50,3 +50,4 @@ export const TreeViewInj: InjectionKey<{
openRenameTableDialog: (table: TableType, rightClick: boolean) => void
contextMenuTarget: { type?: 'project' | 'base' | 'table' | 'main' | 'layout'; value?: any }
}> = Symbol('tree-view-functions-injection')
+export const JsonExpandInj: InjectionKey[> = Symbol('json-expand-injection')
diff --git a/packages/nc-gui/ee/assets/img/fieldPlaceholder.svg b/packages/nc-gui/ee/assets/img/fieldPlaceholder.svg
new file mode 100644
index 0000000000..7fc8286651
--- /dev/null
+++ b/packages/nc-gui/ee/assets/img/fieldPlaceholder.svg
@@ -0,0 +1,196 @@
+
diff --git a/packages/nc-gui/pages/index/[typeOrId]/view/[viewId].vue b/packages/nc-gui/pages/index/[typeOrId]/view/[viewId].vue
index c39c910cdb..0936c0220f 100644
--- a/packages/nc-gui/pages/index/[typeOrId]/view/[viewId].vue
+++ b/packages/nc-gui/pages/index/[typeOrId]/view/[viewId].vue
@@ -13,6 +13,8 @@ const route = useRoute()
const { loadSharedView, meta } = useSharedView()
const { isViewDataLoading } = storeToRefs(useViewsStore())
+provide(MetaInj, meta)
+
const showPassword = ref(false)
onMounted(async () => {
diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts
index 43a75b3309..320e595208 100644
--- a/packages/nc-gui/store/views.ts
+++ b/packages/nc-gui/store/views.ts
@@ -156,6 +156,12 @@ export const useViewsStore = defineStore('viewsStore', () => {
}) => {
const routeName = 'index-typeOrId-projectId-index-index-viewId-viewTitle'
+ let projectIdOrBaseId = projectId
+
+ if (['base'].includes(route.value.params.typeOrId as string)) {
+ projectIdOrBaseId = route.value.params.projectId as string
+ }
+
if (
router.currentRoute.value.query &&
router.currentRoute.value.query.page &&
@@ -163,11 +169,11 @@ export const useViewsStore = defineStore('viewsStore', () => {
) {
await router.push({
name: routeName,
- params: { viewTitle: view.id || '', viewId: tableId, projectId },
+ params: { viewTitle: view.id || '', viewId: tableId, projectId: projectIdOrBaseId },
query: router.currentRoute.value.query,
})
} else {
- await router.push({ name: routeName, params: { viewTitle: view.id || '', viewId: tableId, projectId } })
+ await router.push({ name: routeName, params: { viewTitle: view.id || '', viewId: tableId, projectId: projectIdOrBaseId } })
}
if (hardReload) {
@@ -175,10 +181,14 @@ export const useViewsStore = defineStore('viewsStore', () => {
.replace({
name: routeName,
query: { reload: 'true' },
- params: { viewId: tableId, projectId, viewTitle: view.id || '' },
+ params: { viewId: tableId, projectId: projectIdOrBaseId, viewTitle: view.id || '' },
})
.then(() => {
- router.replace({ name: routeName, query: {}, params: { viewId: tableId, viewTitle: view.id || '', projectId } })
+ router.replace({
+ name: routeName,
+ query: {},
+ params: { viewId: tableId, viewTitle: view.id || '', projectId: projectIdOrBaseId },
+ })
})
}
}
diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts
index 6341018859..8857b6bc29 100644
--- a/packages/nocodb/src/db/BaseModelSqlv2.ts
+++ b/packages/nocodb/src/db/BaseModelSqlv2.ts
@@ -4000,7 +4000,7 @@ class BaseModelSqlv2 {
: await this.dbDriver.raw(query);
// update attachment fields
- data = this.convertAttachmentType(data, childTable);
+ data = await this.convertAttachmentType(data, childTable);
// update date time fields
data = this.convertDateFormat(data, childTable);
@@ -4024,7 +4024,16 @@ class BaseModelSqlv2 {
return d;
}
- public convertAttachmentType(data: Record, childTable?: Model) {
+ public async convertAttachmentType(
+ data: Record,
+ childTable?: Model,
+ ) {
+ if (childTable && !childTable?.columns) {
+ await childTable.getColumns();
+ } else if (!this.model?.columns) {
+ await this.model.getColumns();
+ }
+
// attachment is stored in text and parse in UI
// convertAttachmentType is used to convert the response in string to array of object in API response
if (data) {
diff --git a/packages/nocodb/src/db/conditionV2.ts b/packages/nocodb/src/db/conditionV2.ts
index 6b598a7bc4..a3202b284d 100644
--- a/packages/nocodb/src/db/conditionV2.ts
+++ b/packages/nocodb/src/db/conditionV2.ts
@@ -100,7 +100,11 @@ const parseConditionV2 = async (
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
if (colOptions.type === RelationTypes.HAS_MANY) {
- if (['blank', 'notblank'].includes(filter.comparison_op)) {
+ if (
+ ['blank', 'notblank', 'checked', 'notchecked'].includes(
+ filter.comparison_op,
+ )
+ ) {
// handle self reference
if (parentModel.id === childModel.id) {
if (filter.comparison_op === 'blank') {
@@ -158,7 +162,11 @@ const parseConditionV2 = async (
else qbP.whereIn(parentColumn.column_name, selectQb);
};
} else if (colOptions.type === RelationTypes.BELONGS_TO) {
- if (['blank', 'notblank'].includes(filter.comparison_op)) {
+ if (
+ ['blank', 'notblank', 'checked', 'notchecked'].includes(
+ filter.comparison_op,
+ )
+ ) {
// handle self reference
if (parentModel.id === childModel.id) {
if (filter.comparison_op === 'blank') {
@@ -221,7 +229,11 @@ const parseConditionV2 = async (
const mmParentColumn = await colOptions.getMMParentColumn();
const mmChildColumn = await colOptions.getMMChildColumn();
- if (['blank', 'notblank'].includes(filter.comparison_op)) {
+ if (
+ ['blank', 'notblank', 'checked', 'notchecked'].includes(
+ filter.comparison_op,
+ )
+ ) {
// handle self reference
if (mmModel.id === childModel.id) {
if (filter.comparison_op === 'blank') {
@@ -815,6 +827,7 @@ const negatedMapping = {
nlike: { comparison_op: 'like' },
neq: { comparison_op: 'eq' },
blank: { comparison_op: 'notblank' },
+ notchecked: { comparison_op: 'checked' },
};
function getAlias(aliasCount: { count: number }) {
diff --git a/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts b/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts
index 3286d79ec6..2ce53a0c44 100644
--- a/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts
+++ b/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts
@@ -1619,6 +1619,15 @@ class SqliteClient extends KnexClient {
await this.sqlClient.raw('PRAGMA legacy_alter_table = ON;');
+ /*
+ This is a hack to avoid the following error:
+ - SQLITE_ERROR: duplicate column name: column_name
+
+ Somehow this error is thrown when we drop a column and add a new column with the same name right after it.
+ TODO - Find a better solution for this.
+ */
+ await this.sqlClient.raw('SELECT * FROM ?? LIMIT 1', [args.table]);
+
const trx = await this.sqlClient.transaction();
try {
@@ -1650,7 +1659,7 @@ class SqliteClient extends KnexClient {
await trx.commit();
} catch (e) {
await trx.rollback();
- log.ppe(e, _func);
+ // log.ppe(e, _func);
throw e;
} finally {
if (fkCheckEnabled)
diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts
index 0d57fe554a..ef0265bf22 100644
--- a/packages/nocodb/src/models/Column.ts
+++ b/packages/nocodb/src/models/Column.ts
@@ -925,7 +925,7 @@ export default class Column implements ColumnType {
static async update(
colId: string,
- column: Partial,
+ column: Partial & Partial>,
ncMeta = Noco.ncMeta,
) {
const oldCol = await Column.get({ colId }, ncMeta);
@@ -1079,6 +1079,18 @@ export default class Column implements ColumnType {
await Column.delete(barcodeCol.fk_column_id, ncMeta);
}
}
+ if (
+ column.column_order &&
+ column.column_order.order &&
+ column.column_order.view_id
+ ) {
+ const viewColumn = (
+ await View.getColumns(column.column_order.view_id)
+ ).find((col) => col.fk_column_id === column.id);
+ await View.updateColumn(column.column_order.view_id, viewColumn.id, {
+ order: column.column_order.order,
+ });
+ }
// get existing cache
const key = `${CacheScope.COLUMN}:${colId}`;
diff --git a/packages/nocodb/src/services/datas.service.ts b/packages/nocodb/src/services/datas.service.ts
index e4328abdab..8d1f639218 100644
--- a/packages/nocodb/src/services/datas.service.ts
+++ b/packages/nocodb/src/services/datas.service.ts
@@ -1,16 +1,11 @@
import { Injectable } from '@nestjs/common';
-import { isSystemColumn, UITypes } from 'nocodb-sdk';
+import { isSystemColumn } from 'nocodb-sdk';
import * as XLSX from 'xlsx';
import papaparse from 'papaparse';
import { nocoExecute } from 'nc-help';
import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2';
import type { PathParams } from '~/modules/datas/helpers';
-import type { LinkToAnotherRecordColumn, LookupColumn } from '~/models';
-import {
- getDbRows,
- getViewAndModelByAliasOrId,
- serializeCellValue,
-} from '~/modules/datas/helpers';
+import { getDbRows, getViewAndModelByAliasOrId } from '~/modules/datas/helpers';
import { Base, Column, Model, Project, View } from '~/models';
import { NcError } from '~/helpers/catchError';
import getAst from '~/helpers/getAst';
@@ -946,77 +941,6 @@ export class DatasService {
return { offset, dbRows, elapsed, data };
}
- async serializeCellValue({
- value,
- column,
- siteUrl,
- }: {
- column?: Column;
- value: any;
- siteUrl: string;
- }) {
- if (!column) {
- return value;
- }
-
- if (!value) return value;
-
- switch (column?.uidt) {
- case UITypes.Attachment: {
- let data = value;
- try {
- if (typeof value === 'string') {
- data = JSON.parse(value);
- }
- } catch {}
-
- return (data || []).map(
- (attachment) =>
- `${encodeURI(attachment.title)}(${encodeURI(
- attachment.path
- ? `${siteUrl}/${attachment.path}`
- : attachment.url,
- )})`,
- );
- }
- case UITypes.Lookup:
- {
- const colOptions = await column.getColOptions();
- const lookupColumn = await colOptions.getLookupColumn();
- return (
- await Promise.all(
- [...(Array.isArray(value) ? value : [value])].map(async (v) =>
- serializeCellValue({
- value: v,
- column: lookupColumn,
- siteUrl,
- }),
- ),
- )
- ).join(', ');
- }
- break;
- case UITypes.LinkToAnotherRecord:
- {
- const colOptions =
- await column.getColOptions();
- const relatedModel = await colOptions.getRelatedTable();
- await relatedModel.getColumns();
- return [...(Array.isArray(value) ? value : [value])]
- .map((v) => {
- return v[relatedModel.displayValue?.title];
- })
- .join(', ');
- }
- break;
- default:
- if (value && typeof value === 'object') {
- return JSON.stringify(value);
- }
- return value;
- }
- }
-
async getColumnByIdOrName(columnNameOrId: string, model: Model) {
const column = (await model.getColumns()).find(
(c) =>
diff --git a/packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts b/packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts
index d830effafc..8b6deac4f9 100644
--- a/packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts
+++ b/packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts
@@ -305,7 +305,7 @@ function generalDb() {
cityColumns = await cityTable.getColumns();
});
- it.only('Nested List - Link to another record', async function () {
+ it('Nested List - Link to another record', async function () {
const expectedRecords = [1, 3, 1, 2];
// read first 4 records
@@ -322,7 +322,7 @@ function generalDb() {
expect(cityList).to.deep.equal(expectedRecords);
});
- it.only('Nested List - Lookup', async function () {
+ it('Nested List - Lookup', async function () {
const lookupColumn = await createLookupColumn(context, {
project: sakilaProject,
title: 'Lookup',
@@ -352,7 +352,7 @@ function generalDb() {
expect(lookupData).to.deep.equal(expectedRecords);
});
- it.only('Nested List - Rollup', async function () {
+ it('Nested List - Rollup', async function () {
const rollupColumn = await createRollupColumn(context, {
project: sakilaProject,
title: 'Rollup',
@@ -379,7 +379,7 @@ function generalDb() {
expect(rollupData).to.deep.equal(expectedRecords);
});
- it.only('Nested Read - Link to another record', async function () {
+ it('Nested Read - Link to another record', async function () {
const records = await ncAxiosGet({
url: `/api/v1/tables/${countryTable.id}/rows/1`,
});
@@ -388,7 +388,7 @@ function generalDb() {
expect(+records.body['Cities']).to.equal(1);
});
- it.only('Nested Read - Lookup', async function () {
+ it('Nested Read - Lookup', async function () {
const lookupColumn = await createLookupColumn(context, {
project: sakilaProject,
title: 'Lookup',
@@ -403,7 +403,7 @@ function generalDb() {
expect(records.body.Lookup).to.deep.equal(['Kabul']);
});
- it.only('Nested Read - Rollup', async function () {
+ it('Nested Read - Rollup', async function () {
const rollupColumn = await createRollupColumn(context, {
project: sakilaProject,
title: 'Rollup',
@@ -470,7 +470,7 @@ function textBased() {
/////////////////////////////////////////////////////////////////////////////
- it.only('List: default', async function () {
+ it('List: default', async function () {
const rsp = await ncAxiosGet();
const expectedPageInfo = {
@@ -492,7 +492,7 @@ function textBased() {
);
});
- it.only('List: offset, limit', async function () {
+ it('List: offset, limit', async function () {
const rsp = await ncAxiosGet({ query: { offset: 200, limit: 100 } });
const expectedPageInfo = {
@@ -505,7 +505,7 @@ function textBased() {
expect(rsp.body.pageInfo).to.deep.equal(expectedPageInfo);
});
- it.only('List: fields, single', async function () {
+ it('List: fields, single', async function () {
const rsp = await ncAxiosGet({
query: { fields: 'SingleLineText' },
});
@@ -515,7 +515,7 @@ function textBased() {
).to.equal(true);
});
- it.only('List: fields, multiple', async function () {
+ it('List: fields, multiple', async function () {
const rsp = await ncAxiosGet({
query: { fields: ['SingleLineText', 'MultiLineText'] },
});
@@ -528,7 +528,7 @@ function textBased() {
).to.equal(true);
});
- it.only('List: sort, ascending', async function () {
+ it('List: sort, ascending', async function () {
const sortColumn = columns.find((c) => c.title === 'SingleLineText');
const rsp = await ncAxiosGet({
query: { sort: 'SingleLineText', limit: 400 },
@@ -539,7 +539,7 @@ function textBased() {
expect(sortedArray).to.deep.equal(sortedArray.sort());
});
- it.only('List: sort, descending', async function () {
+ it('List: sort, descending', async function () {
const sortColumn = columns.find((c) => c.title === 'SingleLineText');
const rsp = await ncAxiosGet({
query: { sort: '-SingleLineText', limit: 400 },
@@ -550,7 +550,7 @@ function textBased() {
expect(descSortedArray).to.deep.equal(descSortedArray.sort().reverse());
});
- it.only('List: sort, multiple', async function () {
+ it('List: sort, multiple', async function () {
const rsp = await ncAxiosGet({
query: {
sort: ['-SingleLineText', '-MultiLineText'],
@@ -566,7 +566,7 @@ function textBased() {
expect(sortedArray).to.deep.equal(sortedArray.sort().reverse());
});
- it.only('List: filter, single', async function () {
+ it('List: filter, single', async function () {
const rsp = await ncAxiosGet({
query: {
where: '(SingleLineText,eq,Afghanistan)',
@@ -579,7 +579,7 @@ function textBased() {
expect(filteredArray).to.deep.equal(filteredArray.fill('Afghanistan'));
});
- it.only('List: filter, multiple', async function () {
+ it('List: filter, multiple', async function () {
const rsp = await ncAxiosGet({
query: {
where:
@@ -597,7 +597,7 @@ function textBased() {
);
});
- it.only('List: view ID', async function () {
+ it('List: view ID', async function () {
const gridView = await createView(context, {
title: 'grid0',
table,
@@ -731,7 +731,7 @@ function textBased() {
return gridView;
}
- it.only('List: view ID + sort', async function () {
+ it('List: view ID + sort', async function () {
const gridView = await prepareViewForTests();
const rsp = await ncAxiosGet({
@@ -750,7 +750,7 @@ function textBased() {
expect(sortedArray).to.deep.equal(sortedArray.sort());
});
- it.only('List: view ID + filter', async function () {
+ it('List: view ID + filter', async function () {
const gridView = await prepareViewForTests();
const rsp = await ncAxiosGet({
@@ -769,7 +769,7 @@ function textBased() {
expect(filteredArray).to.deep.equal(filteredArray.fill('1-541-754-3010'));
});
- it.only('List: view ID + fields', async function () {
+ it('List: view ID + fields', async function () {
const gridView = await prepareViewForTests();
const rsp = await ncAxiosGet({
@@ -789,7 +789,7 @@ function textBased() {
});
// Error handling
- it.only('List: invalid ID', async function () {
+ it('List: invalid ID', async function () {
// Invalid table ID
await ncAxiosGet({
url: `/api/v1/tables/123456789/rows`,
@@ -805,7 +805,7 @@ function textBased() {
});
});
- it.only('List: invalid limit & offset', async function () {
+ it('List: invalid limit & offset', async function () {
const expectedPageInfo = {
totalRows: 400,
page: 1,
@@ -858,7 +858,7 @@ function textBased() {
expect(rsp.body.list.length).to.equal(0);
});
- it.only('List: invalid sort, filter, fields', async function () {
+ it('List: invalid sort, filter, fields', async function () {
// expect to ignore invalid sort, filter, fields
await ncAxiosGet({
@@ -892,13 +892,13 @@ function textBased() {
Phone: '1-234-567-8910',
};
- it.only('Create: all fields', async function () {
+ it('Create: all fields', async function () {
const rsp = await ncAxiosPost({ body: newRecord });
expect(rsp.body).to.deep.equal({ Id: 401 });
});
- it.only('Create: few fields left out', async function () {
+ it('Create: few fields left out', async function () {
const newRecord = {
SingleLineText: 'abc',
MultiLineText: 'abc abc \n abc \r abc \t abc 1234!@#$%^&*()_+',
@@ -909,14 +909,14 @@ function textBased() {
expect(rsp.body).to.deep.equal({ Id: 401 });
});
- it.only('Create: bulk', async function () {
+ it('Create: bulk', async function () {
const rsp = await ncAxiosPost({ body: [newRecord, newRecord, newRecord] });
expect(rsp.body).to.deep.equal([{ Id: 401 }, { Id: 402 }, { Id: 403 }]);
});
// Error handling
- it.only('Create: invalid ID', async function () {
+ it('Create: invalid ID', async function () {
// Invalid table ID
await ncAxiosPost({
url: `/api/v1/tables/123456789/rows`,
@@ -944,13 +944,13 @@ function textBased() {
/////////////////////////////////////////////////////////////////////////////
- it.only('Read: all fields', async function () {
+ it('Read: all fields', async function () {
const rsp = await ncAxiosGet({
url: `/api/v1/tables/${table.id}/rows/100`,
});
});
- it.only('Read: invalid ID', async function () {
+ it('Read: invalid ID', async function () {
// Invalid table ID
await ncAxiosGet({
url: `/api/v1/tables/123456789/rows/100`,
@@ -970,7 +970,7 @@ function textBased() {
/////////////////////////////////////////////////////////////////////////////
- it.only('Update: all fields', async function () {
+ it('Update: all fields', async function () {
const rsp = await ncAxiosPatch({
body: [
{
@@ -982,7 +982,7 @@ function textBased() {
expect(rsp.body).to.deep.equal([{ Id: 1 }]);
});
- it.only('Update: partial', async function () {
+ it('Update: partial', async function () {
const recordBeforeUpdate = await ncAxiosGet({
url: `/api/v1/tables/${table.id}/rows/1`,
});
@@ -1008,7 +1008,7 @@ function textBased() {
});
});
- it.only('Update: bulk', async function () {
+ it('Update: bulk', async function () {
const rsp = await ncAxiosPatch({
body: [
{
@@ -1028,7 +1028,7 @@ function textBased() {
// Error handling
- it.only('Update: invalid ID', async function () {
+ it('Update: invalid ID', async function () {
// Invalid table ID
await ncAxiosPatch({
url: `/api/v1/tables/123456789/rows`,
@@ -1049,7 +1049,7 @@ function textBased() {
/////////////////////////////////////////////////////////////////////////////
- it.only('Delete: single', async function () {
+ it('Delete: single', async function () {
const rsp = await ncAxiosDelete({ body: [{ Id: 1 }] });
expect(rsp.body).to.deep.equal([{ Id: 1 }]);
@@ -1060,7 +1060,7 @@ function textBased() {
});
});
- it.only('Delete: bulk', async function () {
+ it('Delete: bulk', async function () {
const rsp = await ncAxiosDelete({ body: [{ Id: 1 }, { Id: 2 }] });
expect(rsp.body).to.deep.equal([{ Id: 1 }, { Id: 2 }]);
@@ -1077,7 +1077,7 @@ function textBased() {
// Error handling
- it.only('Delete: invalid ID', async function () {
+ it('Delete: invalid ID', async function () {
// Invalid table ID
await ncAxiosDelete({
url: `/api/v1/tables/123456789/rows`,
@@ -1224,100 +1224,7 @@ function numberBased() {
},
];
- const recordsPg = [
- {
- Id: 1,
- Number: 33,
- Decimal: 33.3,
- Currency: 33.3,
- Percent: 33,
- Duration: 10,
- Rating: 0,
- },
- {
- Id: 2,
- Number: null,
- Decimal: 456.34,
- Currency: 456.34,
- Percent: null,
- Duration: 20,
- Rating: 1,
- },
- {
- Id: 3,
- Number: 456,
- Decimal: 333.3,
- Currency: 333.3,
- Percent: 456,
- Duration: 30,
- Rating: 2,
- },
- {
- Id: 4,
- Number: 333,
- Decimal: null,
- Currency: null,
- Percent: 333,
- Duration: 40,
- Rating: 3,
- },
- {
- Id: 5,
- Number: 267,
- Decimal: 267.5674,
- Currency: 267.5674,
- Percent: 267,
- Duration: 50,
- Rating: null,
- },
- {
- Id: 6,
- Number: 34,
- Decimal: 34,
- Currency: 34,
- Percent: 34,
- Duration: 60,
- Rating: 0,
- },
- {
- Id: 7,
- Number: 8754,
- Decimal: 8754,
- Currency: 8754,
- Percent: 8754,
- Duration: null,
- Rating: 4,
- },
- {
- Id: 8,
- Number: 3234,
- Decimal: 3234.547,
- Currency: 3234.547,
- Percent: 3234,
- Duration: 70,
- Rating: 5,
- },
- {
- Id: 9,
- Number: 44,
- Decimal: 44.2647,
- Currency: 44.2647,
- Percent: 44,
- Duration: 80,
- Rating: 0,
- },
- {
- Id: 10,
- Number: 33,
- Decimal: 33.98,
- Currency: 33.98,
- Percent: 33,
- Duration: 90,
- Rating: 1,
- },
- ];
-
- it.only('Number based- List & CRUD', async function () {
+ it('Number based- List & CRUD', async function () {
// list 10 records
let rsp = await ncAxiosGet({
query: {
@@ -1332,11 +1239,7 @@ function numberBased() {
isLastPage: false,
};
expect(rsp.body.pageInfo).to.deep.equal(pageInfo);
- if (isPg(context)) {
- expect(rsp.body.list).to.deep.equal(recordsPg);
- } else {
- expect(rsp.body.list).to.deep.equal(records);
- }
+ expect(rsp.body.list).to.deep.equal(records);
///////////////////////////////////////////////////////////////////////////
@@ -1360,11 +1263,7 @@ function numberBased() {
rsp = await ncAxiosGet({
url: `/api/v1/tables/${table.id}/rows/401`,
});
- if (isPg(context)) {
- expect(rsp.body).to.deep.equal({ ...recordsPg[0], Id: 401 });
- } else {
- expect(rsp.body).to.deep.equal({ ...records[0], Id: 401 });
- }
+ expect(rsp.body).to.deep.equal({ ...records[0], Id: 401 });
///////////////////////////////////////////////////////////////////////////
@@ -1377,14 +1276,6 @@ function numberBased() {
Duration: 55,
Rating: 5,
};
- const updatedRecordPg = {
- Number: 55,
- Decimal: 55.5,
- Currency: 55.5,
- Percent: 55,
- Duration: 55,
- Rating: 5,
- };
const updatedRecords = [
{
@@ -1404,25 +1295,6 @@ function numberBased() {
...updatedRecord,
},
];
- const updatedRecordsPg = [
- {
- Id: 401,
- ...updatedRecordPg,
- },
- {
- Id: 402,
- ...updatedRecordPg,
- },
- {
- Id: 403,
- ...updatedRecordPg,
- },
- {
- Id: 404,
- ...updatedRecordPg,
- },
- ];
-
rsp = await ncAxiosPatch({
body: updatedRecords,
});
@@ -1437,11 +1309,8 @@ function numberBased() {
offset: 400,
},
});
- if (isPg(context)) {
- expect(rsp.body.list).to.deep.equal(updatedRecordsPg);
- } else {
- expect(rsp.body.list).to.deep.equal(updatedRecords);
- }
+
+ expect(rsp.body.list).to.deep.equal(updatedRecords);
///////////////////////////////////////////////////////////////////////////
@@ -1546,7 +1415,7 @@ function selectBased() {
},
];
- it.only('Select based- List & CRUD', async function () {
+ it('Select based- List & CRUD', async function () {
// list 10 records
let rsp = await ncAxiosGet({
query: {
@@ -1679,7 +1548,7 @@ function dateBased() {
expect(insertedRecords.length).to.equal(800);
});
- it.only('Date based- List & CRUD', async function () {
+ it('Date based- List & CRUD', async function () {
// list 10 records
let rsp = await ncAxiosGet({
query: {
@@ -1909,7 +1778,7 @@ function linkBased() {
return columns.find((c) => c.title === title).id;
}
- it.only('Has-Many ', async function () {
+ it('Has-Many ', async function () {
// Create hm link between Country and City
await ncAxiosLinkAdd({
urlParams: {
@@ -2070,7 +1939,7 @@ function linkBased() {
return Array.from({ length: count }, (_, index) => i + index);
}
- it.only('Create Many-Many ', async function () {
+ it('Create Many-Many ', async function () {
await ncAxiosLinkAdd({
urlParams: {
tableId: tblActor.id,
@@ -2244,7 +2113,7 @@ function linkBased() {
// Other scenarios
// Has-many : change an existing link to a new one
- it.only('HM: Change an existing link to a new one', async function () {
+ it('HM: Change an existing link to a new one', async function () {
// add a link
await ncAxiosLinkAdd({
urlParams: {
@@ -2291,7 +2160,7 @@ function linkBased() {
});
// limit & offset verification
- it.only('Limit & offset verification', async function () {
+ it('Limit & offset verification', async function () {
// add a link
await ncAxiosLinkAdd({
urlParams: {
@@ -2599,7 +2468,7 @@ function linkBased() {
}
// Error handling (has-many)
- it.only('Error handling : HM: Nested ADD', async function () {
+ it('Error handling : HM: Nested ADD', async function () {
const validParams = {
urlParams: {
tableId: tblCountry.id,
@@ -2613,7 +2482,7 @@ function linkBased() {
await nestedAddTests(validParams);
});
- it.only('Error handling : HM: Nested REMOVE', async function () {
+ it('Error handling : HM: Nested REMOVE', async function () {
// Prepare data
await ncAxiosLinkAdd({
urlParams: {
@@ -2638,7 +2507,7 @@ function linkBased() {
await nestedRemoveTests(validParams);
});
- it.only('Error handling : HM: Nested List', async function () {
+ it('Error handling : HM: Nested List', async function () {
// Prepare data
await ncAxiosLinkAdd({
urlParams: {
@@ -2667,7 +2536,7 @@ function linkBased() {
});
// Error handling (belongs to)
- it.only('Error handling : BT: Nested ADD', async function () {
+ it('Error handling : BT: Nested ADD', async function () {
const validParams = {
urlParams: {
tableId: tblCity.id,
@@ -2681,7 +2550,7 @@ function linkBased() {
await nestedAddTests(validParams, 'bt');
});
- it.only('Error handling : BT: Nested REMOVE', async function () {
+ it('Error handling : BT: Nested REMOVE', async function () {
// Prepare data
await ncAxiosLinkAdd({
urlParams: {
@@ -2706,7 +2575,7 @@ function linkBased() {
await nestedRemoveTests(validParams, 'bt');
});
- it.only('Error handling : BT: Nested List', async function () {
+ it('Error handling : BT: Nested List', async function () {
// Prepare data
await ncAxiosLinkAdd({
urlParams: {
@@ -2735,7 +2604,7 @@ function linkBased() {
});
// Error handling (many-many)
- it.only('Error handling : MM: Nested ADD', async function () {
+ it('Error handling : MM: Nested ADD', async function () {
const validParams = {
urlParams: {
tableId: tblActor.id,
@@ -2749,7 +2618,7 @@ function linkBased() {
await nestedAddTests(validParams);
});
- it.only('Error handling : MM: Nested REMOVE', async function () {
+ it('Error handling : MM: Nested REMOVE', async function () {
// Prepare data
await ncAxiosLinkAdd({
urlParams: {
@@ -2774,7 +2643,7 @@ function linkBased() {
await nestedRemoveTests(validParams);
});
- it.only('Error handling : MM: Nested List', async function () {
+ it('Error handling : MM: Nested List', async function () {
// Prepare data
await ncAxiosLinkAdd({
urlParams: {
diff --git a/packages/nocodb/tests/unit/rest/tests/org.test.ts b/packages/nocodb/tests/unit/rest/tests/org.test.ts
index ce4805318b..5368e13813 100644
--- a/packages/nocodb/tests/unit/rest/tests/org.test.ts
+++ b/packages/nocodb/tests/unit/rest/tests/org.test.ts
@@ -172,7 +172,7 @@ function authTests() {
expect(response.body.list).to.have.length(0);
});
- it.only('Disable/Enable signup', async () => {
+ it('Disable/Enable signup', async () => {
const args = {
email: 'dummyuser@example.com',
password: 'A1234abh2@dsad',
diff --git a/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts b/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts
index 720f09baa6..ceb6bcfd9e 100644
--- a/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts
+++ b/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts
@@ -1924,7 +1924,7 @@ function tableTest() {
});
// todo: Integrate filterArrJson with bulk delete all and update all
- // it.only('Bulk delete all with condition', async function () {
+ // it('Bulk delete all with condition', async function () {
// const table = await createTable(context, project);
// const columns = await table.getColumns();
// const idColumn = columns.find((column) => column.title === 'Id')!;
@@ -1960,11 +1960,11 @@ function tableTest() {
// });
// todo: add test for bulk delete with ltar but need filterArrJson. filterArrJson not now supported with this api.
- // it.only('Bulk update nested filtered table data list with a lookup column', async function () {
+ // it('Bulk update nested filtered table data list with a lookup column', async function () {
// });
// todo: Api does not support fields and sort
- // it.only('Nested row list hm with selected fields', async () => {
+ // it('Nested row list hm with selected fields', async () => {
// const rowId = 1;
// const firstNameColumn = customerColumns.find(
@@ -1994,7 +1994,7 @@ function tableTest() {
// })
// todo: mm create api does not error out in the case of existing ref row id
- // it.only('Create list mm existing ref row id', async () => {
+ // it('Create list mm existing ref row id', async () => {
// const rowId = 1;
// const rentalListColumn = (await customerTable.getColumns()).find(
// (column) => column.title === 'Rentals'
diff --git a/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts b/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts
index f27e2178b7..232ab022ed 100644
--- a/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts
+++ b/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts
@@ -893,7 +893,7 @@ function viewRowTests() {
};
// todo: gallery view doesnt seem to support rollup
- // it.only('Find one sorted filtered view with nested fields data list with a rollup column in customer table GALLERY', async function () {
+ // it('Find one sorted filtered view with nested fields data list with a rollup column in customer table GALLERY', async function () {
// await testFindOneSortedFilteredNestedFieldsDataWithRollup(ViewTypes.GALLERY);
// });
diff --git a/tests/playwright/pages/Dashboard/Grid/index.ts b/tests/playwright/pages/Dashboard/Grid/index.ts
index 919cf28d0a..40e4b56f3d 100644
--- a/tests/playwright/pages/Dashboard/Grid/index.ts
+++ b/tests/playwright/pages/Dashboard/Grid/index.ts
@@ -272,7 +272,7 @@ export class GridPage extends BasePage {
await this.get().locator('[data-testid="nc-check-all"]').nth(0).click({
button: 'right',
});
- await this.rootPage.locator('text=Update Selected Rows').click();
+ await this.rootPage.locator('.nc-menu-item:has-text("Update Selected Rows")').click();
await this.dashboard.waitForLoaderToDisappear();
}
diff --git a/tests/playwright/pages/Dashboard/ViewSidebar/index.ts b/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
index 7e9d37e862..856ab562ff 100644
--- a/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
+++ b/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
@@ -160,7 +160,7 @@ export class ViewSidebarPage extends BasePage {
force: true,
});
const submitAction = () =>
- this.rootPage.locator('.ant-modal-content').locator('button:has-text("Create View"):visible').click();
+ this.rootPage.locator('.ant-modal-content').locator('button:has-text("Create a View"):visible').click();
await this.waitForResponse({
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/',
diff --git a/tests/playwright/pages/Dashboard/common/Topbar/Share.ts b/tests/playwright/pages/Dashboard/common/Topbar/Share.ts
index 143d7c7a9f..af07785484 100644
--- a/tests/playwright/pages/Dashboard/common/Topbar/Share.ts
+++ b/tests/playwright/pages/Dashboard/common/Topbar/Share.ts
@@ -56,10 +56,7 @@ export class TopbarSharePage extends BasePage {
.locator('.ant-switch')
.nth(0)
.click({
- position: {
- x: 4,
- y: 4,
- },
+ position: { x: 4, y: 4 },
});
}
]