Browse Source

Merge pull request #9958 from nocodb/nc-fix/bug-fix-4-dec

fix(nocodb): Bug fixes
pull/9993/head
Ramesh Mane 5 days ago committed by GitHub
parent
commit
a07086428b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      packages/nc-gui/components/cell/SingleSelect.vue
  2. 12
      packages/nc-gui/components/cell/TextArea.vue
  3. 29
      packages/nc-gui/components/smartsheet/Form.vue
  4. 53
      packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
  5. 2
      packages/nc-gui/components/smartsheet/column/FormulaInputHelper.vue
  6. 24
      packages/nc-gui/components/smartsheet/column/FormulaOptions.vue
  7. 32
      packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue
  8. 33
      packages/nc-gui/composables/useViewAggregate.ts
  9. 8
      packages/nocodb/src/models/Column.ts

8
packages/nc-gui/components/cell/SingleSelect.vue

@ -482,8 +482,12 @@ const onFocus = () => {
text-overflow: clip;
}
:deep(.ant-select-selection-search-input) {
@apply !text-xs;
:deep(.ant-select-selection-search) {
@apply flex items-center;
.ant-select-selection-search-input {
@apply !text-xs;
}
}
:deep(.ant-select-clear > span) {

12
packages/nc-gui/components/cell/TextArea.vue

@ -664,6 +664,18 @@ textarea:focus {
@apply !block cursor-pointer;
}
.nc-grid-cell {
&.align-top {
.long-text-wrapper {
@apply items-start;
}
}
&:not(.align-top) {
@apply items-center;
}
}
.nc-data-cell {
&:has(.nc-cell-ai .nc-expanded-form-open) {
@apply !border-none -mx-1 -my-1;

29
packages/nc-gui/components/smartsheet/Form.vue

@ -655,18 +655,34 @@ const handleOnUploadImage = (data: AttachmentResType = null) => {
updateView()
}
const isFocusedFieldLabel = ref(false)
const onFocusActiveFieldLabel = (e: FocusEvent) => {
isFocusedFieldLabel.value = true
if (activeField.value && !activeField.value.label) {
activeField.value.label = activeField.value?.title ?? ''
}
;(e.target as HTMLTextAreaElement).select()
}
const activeFieldLabel = computed(() => {
if (!isFocusedFieldLabel.value && !activeField.value?.label) {
return activeField.value?.title
}
return activeField.value?.label ?? ''
})
onClickOutside(focusLabel, () => {
isFocusedFieldLabel.value = false
})
const updateFieldTitle = (value: string) => {
if (!activeField.value) return
if (activeField.value.title === value) {
activeField.value.label = null
} else {
activeField.value.label = value
}
activeField.value.label = value.trimStart()
}
const handleAutoScrollFormField = (title: string, isSidebar: boolean) => {
@ -1459,7 +1475,7 @@ useEventListener(
<a-textarea
ref="focusLabel"
:value="activeField.label || activeField.title"
:value="activeFieldLabel"
:rows="1"
auto-size
hide-details
@ -1467,6 +1483,7 @@ useEventListener(
data-testid="nc-form-input-label"
:placeholder="$t('msg.info.formInput')"
@focus="onFocusActiveFieldLabel"
@blur="isFocusedFieldLabel = false"
@keydown.enter.prevent
@input="updateFieldTitle($event.target.value)"
@change="updateColMeta(activeField)"

53
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue

@ -238,6 +238,20 @@ const uiTypesOptions = computed<typeof uiTypes>(() => {
return types
})
const editOrAddRef = ref<HTMLDivElement>()
const isScrollEnabled = ref(false)
const handleScrollDebounce = useDebounceFn(() => {
if (props.fromTableExplorer || !editOrAddRef.value || aiAutoSuggestMode.value) return
if (editOrAddRef.value.clientHeight < editOrAddRef.value.scrollHeight) {
isScrollEnabled.value = true
} else {
isScrollEnabled.value = false
}
}, 500)
const onSelectType = (uidt: UITypes | typeof AIButton, fromSearchList = false) => {
let preload
@ -254,6 +268,10 @@ const onSelectType = (uidt: UITypes | typeof AIButton, fromSearchList = false) =
formState.value.uidt = uidt
}
onUidtOrIdTypeChange(preload)
nextTick(() => {
handleScrollDebounce()
})
}
const reloadMetaAndData = async () => {
@ -393,6 +411,9 @@ onMounted(() => {
nextTick(() => {
mounted.value = true
emit('mounted')
handleScrollDebounce()
if (!isEdit.value) {
if (!formState.value?.temp_id) {
emit('add', formState.value)
@ -489,6 +510,9 @@ const triggerDescriptionEnable = () => {
descInputEl.value?.focus()
}, 100)
}
nextTick(() => {
handleScrollDebounce()
})
}
const isFullUpdateAllowed = computed(() => {
@ -597,6 +621,7 @@ watch(activeAiTab, (newValue) => {
<template>
<div
v-if="!warningVisible"
ref="editOrAddRef"
class="overflow-auto nc-scrollbar-md"
:class="{
'bg-white max-h-[max(80vh,500px)]': !props.fromTableExplorer,
@ -604,13 +629,15 @@ watch(activeAiTab, (newValue) => {
'min-w-[500px]': formState.uidt === UITypes.LinkToAnotherRecord || formState.uidt === UITypes.Links,
'!w-[600px]': formState.uidt === UITypes.LinkToAnotherRecord || formState.uidt === UITypes.Links,
'min-w-[422px] !w-full': isLinksOrLTAR(formState.uidt),
'shadow-lg shadow-gray-300 border-1 border-gray-200 rounded-xl p-5': !embedMode,
'shadow-lg shadow-gray-300 border-1 border-gray-200 rounded-2xl p-5': !embedMode,
'nc-ai-mode': isAiMode,
'h-full': props.fromTableExplorer,
'!bg-nc-bg-gray-extralight': aiAutoSuggestMode && formState.uidt && !props.fromTableExplorer,
'!pb-0': !embedMode && !aiAutoSuggestMode && formState.uidt,
}"
@keydown="handleEscape"
@click.stop
@scroll="handleScrollDebounce"
>
<a-form
v-model="formState"
@ -1227,7 +1254,12 @@ watch(activeAiTab, (newValue) => {
</Transition>
</template>
<a-form-item v-if="enableDescription && !aiAutoSuggestMode">
<a-form-item
v-if="enableDescription && !aiAutoSuggestMode"
:class="{
'!pb-4': embedMode,
}"
>
<div class="flex gap-3 text-gray-800 h-7 mb-1 items-center justify-between">
<span class="text-[13px]">
{{ $t('labels.description') }}
@ -1254,8 +1286,13 @@ watch(activeAiTab, (newValue) => {
</a-form-item>
<template v-if="props.fromTableExplorer">
<a-form-item>
<NcButton v-if="!enableDescription" size="small" type="text" @click.stop="triggerDescriptionEnable">
<a-form-item
v-if="!enableDescription"
:class="{
'!pb-4': embedMode,
}"
>
<NcButton size="small" type="text" @click.stop="triggerDescriptionEnable">
<div class="flex !text-gray-700 items-center gap-2">
<GeneralIcon icon="plus" class="h-4 w-4" />
@ -1266,8 +1303,14 @@ watch(activeAiTab, (newValue) => {
</NcButton>
</a-form-item>
</template>
<template v-else>
<div class="flex items-center justify-between gap-2 empty:hidden">
<div
class="flex items-center justify-between gap-2 empty:hidden sticky bottom-0 z-10 bg-white px-5 pb-5 -mx-5"
:class="{
'border-t-1 border-nc-border-gray-medium pt-3': isScrollEnabled,
}"
>
<NcButton v-if="!enableDescription" size="small" type="text" @click.stop="triggerDescriptionEnable">
<div class="flex !text-gray-700 items-center gap-2">
<GeneralIcon icon="plus" class="h-4 w-4" />

2
packages/nc-gui/components/smartsheet/column/FormulaInputHelper.vue

@ -638,7 +638,7 @@ const enableAI = async () => {
<template>
<div
v-if="suggestionPreviewed && !suggestionPreviewed.unsupported && suggestionPreviewed.type === 'function'"
class="w-84 fixed bg-white z-10 pl-3 pt-3 border-1 shadow-md rounded-xl"
class="w-84 fixed bg-white z-11 pl-3 pt-3 border-1 shadow-md rounded-xl"
:style="{
left: suggestionPreviewPostion.left,
top: suggestionPreviewPostion.top,

24
packages/nc-gui/components/smartsheet/column/FormulaOptions.vue

@ -118,6 +118,10 @@ const debouncedValidate = useDebounceFn(async () => {
dataType: FormulaDataTypes.UNKNOWN,
}
}
} finally {
if (vModel.value?.colOptions?.parsed_tree?.dataType !== parsedTree.value?.dataType) {
vModel.value.meta.display_type = null
}
}
}, 300)
@ -175,15 +179,6 @@ watch(
immediate: true,
},
)
watch(parsedTree, (value, oldValue) => {
if (oldValue === undefined && value) {
return
}
if (value?.dataType !== oldValue?.dataType) {
vModel.value.meta.display_type = null
}
})
</script>
<template>
@ -209,9 +204,14 @@ watch(parsedTree, (value, oldValue) => {
<div>{{ $t('labels.formatting') }}</div>
</div>
</template>
<div class="flex flex-col px-0.5 gap-4">
<div class="flex flex-col px-0.5 gap-4 pb-0.5">
<a-form-item class="mt-4" :label="$t('general.format')">
<a-select v-model:value="vModel.meta.display_type" class="w-full" :placeholder="$t('labels.selectAFormatType')">
<NcSelect
v-model:value="vModel.meta.display_type"
class="w-full nc-select-shadow"
:placeholder="$t('labels.selectAFormatType')"
allow-clear
>
<a-select-option v-for="option in supportedFormulaAlias" :key="option.value" :value="option.value">
<div class="flex w-full items-center gap-2 justify-between">
<div class="w-full">
@ -226,7 +226,7 @@ watch(parsedTree, (value, oldValue) => {
/>
</div>
</a-select-option>
</a-select>
</NcSelect>
</a-form-item>
<template

32
packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue

@ -665,15 +665,13 @@ const onActiveCellChanged = () => {
}
}
const isOpen = ref(false)
const isDeleteAllModalIsOpen = ref(false)
async function deleteAllRecords() {
isDeleteAllModalIsOpen.value = true
function closeDlg() {
isOpen.value = false
close(200)
}
const { close } = useDialog(resolveComponent('DlgRecordDeleteAll'), {
'modelValue': isDeleteAllModalIsOpen,
'rows': totalRows.value,
@ -685,10 +683,14 @@ async function deleteAllRecords() {
},
})
function closeDlg() {
isOpen.value = false
close(200)
}
await until(isDeleteAllModalIsOpen).toBe(false)
}
const isOpen = ref(false)
async function expandRows({
newRows,
newColumns,
@ -1878,9 +1880,7 @@ watch(vSelectedAllRecords, (selectedAll) => {
data-testid="nc-check-all"
class="flex items-center pl-2 pr-1 w-full h-full justify-center"
>
<div class="nc-no-label text-gray-500" :class="{ hidden: vSelectedAllRecords }">
#
</div>
<div class="nc-no-label text-gray-500" :class="{ hidden: vSelectedAllRecords }">#</div>
<div
:class="{
hidden: !vSelectedAllRecords,
@ -2145,13 +2145,10 @@ watch(vSelectedAllRecords, (selectedAll) => {
top: `${(index + 1) * rowHeight - 6}px`,
zIndex: 100001,
}"
class="absolute z-30 left-0"
class="absolute z-30 left-0 w-full flex"
>
<div
class="flex items-center gap-2 transform bg-yellow-500 px-2 py-1 rounded-br-md font-semibold text-xs text-gray-800"
:style="{
transform: `translateX(${scrollLeft - leftOffset}px)`,
}"
class="sticky left-0 flex items-center gap-2 transform bg-yellow-500 px-2 py-1 rounded-br-md font-semibold text-xs text-gray-800"
>
Row filtered
@ -2170,13 +2167,10 @@ watch(vSelectedAllRecords, (selectedAll) => {
top: `${(index + 1) * rowHeight - 6}px`,
zIndex: 100000,
}"
class="absolute transform z-30 left-0"
class="absolute transform z-30 left-0 w-full flex"
>
<div
class="flex items-center gap-2 transform bg-yellow-500 px-2 py-1 rounded-br-md font-semibold text-xs text-gray-800"
:style="{
transform: `translateX(${scrollLeft - leftOffset}px)`,
}"
class="sticky left-0 flex items-center gap-2 transform bg-yellow-500 px-2 py-1 rounded-br-md font-semibold text-xs text-gray-800"
>
Row moved

33
packages/nc-gui/composables/useViewAggregate.ts

@ -2,6 +2,7 @@ import type { Ref } from 'vue'
import {
type ColumnType,
CommonAggregations,
type FormulaType,
type TableType,
UITypes,
type ViewType,
@ -123,6 +124,32 @@ const [useProvideViewAggregate, useViewAggregate] = useInjectionState(
await updateGridViewColumn(fieldId, { aggregation: agg })
}
const aggregateFormulaFields = computed(() => {
return fields.value
.filter((field) => {
if (!field?.id || !field?.title) return false
if (
!isFormula(field) ||
!gridViewCols.value[field.id] ||
!gridViewCols.value[field.id]?.aggregation ||
gridViewCols.value[field.id]?.aggregation === CommonAggregations.None ||
!(field.colOptions as FormulaType)?.formula_raw
) {
return false
}
return true
})
.map((field) => {
return {
id: field.id,
aggregation: gridViewCols.value[field.id!]?.aggregation ?? CommonAggregations.None,
formula_raw: (field.colOptions as FormulaType)?.formula_raw ?? '',
}
})
})
reloadAggregate?.on(async (_fields) => {
if (!_fields || !_fields?.fields.length) {
await loadViewAggregate()
@ -135,6 +162,12 @@ const [useProvideViewAggregate, useViewAggregate] = useInjectionState(
acc[f.id] = field.aggregation ?? gridViewCols.value[f.id].aggregation ?? CommonAggregations.None
for (const formulaField of aggregateFormulaFields.value) {
if (!acc[formulaField.id!] && formulaField.formula_raw.includes(field.title)) {
acc[formulaField.id!] = formulaField.aggregation
}
}
return acc
}, {} as Record<string, string>)

8
packages/nocodb/src/models/Column.ts

@ -1357,9 +1357,13 @@ export default class Column<T = any> implements ColumnType {
);
}
if (oldCol.uidt === UITypes.Attachment && oldCol.uidt !== column.uidt) {
if (
column.uidt &&
oldCol.uidt === UITypes.Attachment &&
oldCol.uidt !== column.uidt
) {
// Set Gallery & Kanban view `fk_cover_image_col_id` value to null
await Column.deleteCoverImageColumnId(context, column.id, ncMeta);
await Column.deleteCoverImageColumnId(context, colId, ncMeta);
}
// set meta

Loading…
Cancel
Save