mirror of
https://github.com/nocodb/nocodb.git
synced 2026-04-25 04:26:10 +00:00
Nc fix/UI flaws p2 (#8394)
* fix: avoid data reload on field hide & rearrange * fix: load rollup_function on edit * fix: hide field if no rollup fn is available * fix: allowed rollup fns * fix: multiselect blur on escape * test: skip wait for update from server for hide field ops * fix: skip wait for response for field toggle * fix: disable validation only for hide field * fix: error handling * fix: base name missing on dashboard * fix: replace slash with arrow * fix: table expand icon & animation * fix: view chevron * fix: chevron for base and chevron sizes --------- Co-authored-by: mertmit <mertmit99@gmail.com>
This commit is contained in:
@@ -221,7 +221,9 @@ watch(isOpen, (n, _o) => {
|
||||
if (!n) searchVal.value = ''
|
||||
|
||||
if (editAllowed.value) {
|
||||
if (n) {
|
||||
if (!n) {
|
||||
aselect.value?.$el?.querySelector('input')?.blur()
|
||||
} else {
|
||||
aselect.value?.$el?.querySelector('input')?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,9 +632,9 @@ const onTableIdCopy = async () => {
|
||||
@click="onProjectClick(base, true, true)"
|
||||
>
|
||||
<GeneralIcon
|
||||
icon="chevronDown"
|
||||
class="group-hover:visible cursor-pointer transform transition-transform duration-500 rotate-270"
|
||||
:class="{ '!rotate-180': base.isExpanded }"
|
||||
icon="chevronRight"
|
||||
class="group-hover:visible cursor-pointer transform transition-transform duration-200 text-[20px]"
|
||||
:class="{ '!rotate-90': base.isExpanded }"
|
||||
/>
|
||||
</NcButton>
|
||||
</template>
|
||||
|
||||
@@ -395,9 +395,9 @@ const deleteTable = () => {
|
||||
@click.stop="onExpand"
|
||||
>
|
||||
<GeneralIcon
|
||||
icon="chevronDown"
|
||||
class="nc-sidebar-source-node-btns cursor-pointer transform transition-transform duration-500 !text-gray-600 rotate-270"
|
||||
:class="{ '!rotate-180': isExpanded }"
|
||||
icon="chevronRight"
|
||||
class="nc-sidebar-source-node-btns cursor-pointer transform transition-transform duration-200 !text-gray-600 text-[20px]"
|
||||
:class="{ '!rotate-90': isExpanded }"
|
||||
/>
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ const route = router.currentRoute
|
||||
|
||||
const { $e, $api } = useNuxtApp()
|
||||
|
||||
const currentBase = computed(async () => {
|
||||
const currentBase = computedAsync(async () => {
|
||||
let base
|
||||
if (props.baseId) {
|
||||
base = bases.value.get(props.baseId)
|
||||
@@ -161,7 +161,7 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ProjectAccessSettings :base-id="currentBase.id" />
|
||||
<ProjectAccessSettings :base-id="currentBase?.id" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane v-if="isUIAllowed('sourceCreate')" key="data-source">
|
||||
<template #tab>
|
||||
|
||||
@@ -56,13 +56,16 @@ const refTables = computed(() => {
|
||||
|
||||
const _refTables = meta.value.columns
|
||||
.filter(
|
||||
(c) =>
|
||||
(c: ColumnType) =>
|
||||
isLinksOrLTAR(c) &&
|
||||
![RelationTypes.BELONGS_TO, RelationTypes.ONE_TO_ONE].includes((c.colOptions as LinkToAnotherRecordType).type) &&
|
||||
(c.colOptions as LinkToAnotherRecordType).type &&
|
||||
![RelationTypes.BELONGS_TO, RelationTypes.ONE_TO_ONE].includes(
|
||||
(c.colOptions as LinkToAnotherRecordType).type as RelationTypes,
|
||||
) &&
|
||||
!c.system &&
|
||||
c.source_id === meta.value?.source_id,
|
||||
)
|
||||
.map((c) => ({
|
||||
.map((c: ColumnType) => ({
|
||||
col: c.colOptions,
|
||||
column: c,
|
||||
...tables.value.find((t) => t.id === (c.colOptions as any)?.fk_related_model_id),
|
||||
@@ -118,16 +121,29 @@ const allFunctions = [
|
||||
{ text: t('general.avgDistinct'), value: 'avgDistinct' },
|
||||
]
|
||||
|
||||
const availableRollupPerColumn = computed(() => {
|
||||
const fnMap: Record<string, { text: string; value: string }[]> = {}
|
||||
columns.value?.forEach((column) => {
|
||||
if (!column?.id) return
|
||||
fnMap[column.id] = allFunctions.filter((func) => getAvailableRollupForUiType(column.uidt as UITypes).includes(func.value))
|
||||
})
|
||||
return fnMap
|
||||
})
|
||||
|
||||
const filteredColumns = computed(() => {
|
||||
return columns.value?.filter((column) => {
|
||||
return column.id && availableRollupPerColumn.value[column.id as string]?.length
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
() => vModel.value.fk_rollup_column_id,
|
||||
() => {
|
||||
const childFieldColumn = columns.value?.find((column: ColumnType) => column.id === vModel.value.fk_rollup_column_id)
|
||||
|
||||
aggFunctionsList.value = allFunctions.filter((func) =>
|
||||
getAvailableRollupForUiType(childFieldColumn?.uidt as UITypes).includes(func.value),
|
||||
)
|
||||
aggFunctionsList.value = availableRollupPerColumn.value[childFieldColumn?.id as string] || []
|
||||
|
||||
if (!aggFunctionsList.value.includes(vModel.value.rollup_function)) {
|
||||
if (aggFunctionsList.value.length && !aggFunctionsList.value.find((func) => func.value === vModel.value.rollup_function)) {
|
||||
// when the previous roll up function was numeric type and the current child field is non-numeric
|
||||
// reset rollup function with a non-numeric type
|
||||
vModel.value.rollup_function = aggFunctionsList.value[0].value
|
||||
@@ -176,7 +192,7 @@ watch(
|
||||
dropdown-class-name="nc-dropdown-relation-column !rounded-xl"
|
||||
@change="onDataTypeChange"
|
||||
>
|
||||
<a-select-option v-for="(column, index) of columns" :key="index" :value="column.id">
|
||||
<a-select-option v-for="(column, index) of filteredColumns" :key="index" :value="column.id">
|
||||
<div class="flex gap-2 truncate items-center">
|
||||
<div class="flex items-center flex-1 truncate font-semibold">
|
||||
<component :is="cellIcon(column)" :column-meta="column" />
|
||||
|
||||
@@ -133,9 +133,6 @@ const onMove = async (_event: { moved: { newIndex: number; oldIndex: number } },
|
||||
)
|
||||
|
||||
await loadViewColumns()
|
||||
await reloadViewDataHook?.trigger({
|
||||
shouldShowLoading: false,
|
||||
})
|
||||
|
||||
$e('a:fields:reorder')
|
||||
} catch (e) {
|
||||
|
||||
@@ -173,7 +173,7 @@ function openDeleteDialog() {
|
||||
>
|
||||
<div
|
||||
v-e="['c:breadcrumb:view-actions']"
|
||||
class="truncate nc-active-view-title flex gap-0.5 items-center !hover:(bg-gray-100 text-gray-800) ml-1 pl-1 pr-0.25 rounded-md py-1 cursor-pointer"
|
||||
class="truncate nc-active-view-title flex gap-1 items-center !hover:(bg-gray-100 text-gray-800) ml-1 pl-1 pr-0.25 rounded-md py-1 cursor-pointer"
|
||||
:class="{
|
||||
'max-w-2/5': !isSharedBase && !isMobileMode && activeView?.is_default,
|
||||
'max-w-3/5': !isSharedBase && !isMobileMode && !activeView?.is_default,
|
||||
@@ -192,7 +192,7 @@ function openDeleteDialog() {
|
||||
{{ activeView?.is_default ? $t('title.defaultView') : activeView?.title }}
|
||||
</span>
|
||||
</NcTooltip>
|
||||
<GeneralIcon icon="arrowDown" class="ml-1" />
|
||||
<GeneralIcon icon="chevronDown" class="!text-gray-500 mt-0.5" />
|
||||
</div>
|
||||
<template #overlay>
|
||||
<SmartsheetToolbarViewActionMenu
|
||||
|
||||
@@ -69,7 +69,7 @@ const openedBaseUrl = computed(() => {
|
||||
</div>
|
||||
</NcTooltip>
|
||||
</NuxtLink>
|
||||
<div class="px-1.75 text-gray-500">/</div>
|
||||
<div class="px-1.75 text-gray-500">></div>
|
||||
</template>
|
||||
<template v-if="!(isMobileMode && !activeView?.is_default)">
|
||||
<LazyGeneralEmojiPicker v-if="isMobileMode" :emoji="activeTable?.meta?.icon" readonly size="xsmall">
|
||||
@@ -122,7 +122,7 @@ const openedBaseUrl = computed(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="!isMobileMode" class="pl-1.25 text-gray-500">/</div>
|
||||
<div v-if="!isMobileMode" class="pl-1.25 text-gray-500">></div>
|
||||
|
||||
<template v-if="!(isMobileMode && activeView?.is_default)">
|
||||
<LazyGeneralEmojiPicker v-if="isMobileMode" :emoji="activeView?.meta?.icon" readonly size="xsmall">
|
||||
|
||||
@@ -286,7 +286,7 @@ const [useProvideViewColumns, useViewColumns] = useInjectionState(
|
||||
},
|
||||
scope: defineViewScope({ view: view.value }),
|
||||
})
|
||||
saveOrUpdate(field, fieldIndex)
|
||||
saveOrUpdate(field, fieldIndex, !checked)
|
||||
}
|
||||
|
||||
const toggleFieldStyles = (field: any, style: 'underline' | 'bold' | 'italic', status: boolean) => {
|
||||
|
||||
@@ -87,9 +87,13 @@ const getAvailableRollupForUiType = (type: string) => {
|
||||
UITypes.Email,
|
||||
UITypes.PhoneNumber,
|
||||
UITypes.URL,
|
||||
UITypes.Checkbox,
|
||||
UITypes.JSON,
|
||||
].includes(type as UITypes)
|
||||
) {
|
||||
return ['count'];
|
||||
} else if ([UITypes.Attachment].includes(type as UITypes)) {
|
||||
return [];
|
||||
} else {
|
||||
return [
|
||||
'sum',
|
||||
|
||||
@@ -31,22 +31,24 @@ export class ToolbarFieldsPage extends BasePage {
|
||||
// hack
|
||||
await this.rootPage.waitForTimeout(100);
|
||||
|
||||
// toggle only if input checked value is not equal to given checked value
|
||||
await this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').scrollIntoViewIfNeeded();
|
||||
const isChecked = await this.get()
|
||||
.locator(`[data-testid="nc-fields-menu-${title}"]`)
|
||||
.locator('.nc-switch')
|
||||
.isChecked();
|
||||
if (checked !== undefined) {
|
||||
// toggle only if input checked value is not equal to given checked value
|
||||
await this.get()
|
||||
.locator(`[data-testid="nc-fields-menu-${title}"]`)
|
||||
.locator('.nc-switch')
|
||||
.scrollIntoViewIfNeeded();
|
||||
const isChecked = await this.get()
|
||||
.locator(`[data-testid="nc-fields-menu-${title}"]`)
|
||||
.locator('.nc-switch')
|
||||
.isChecked();
|
||||
if ((checked && isChecked) || (!checked && !isChecked)) {
|
||||
await this.toolbar.clickFields();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isChecked === true) {
|
||||
// disable response validation for hide field
|
||||
validateResponse = false;
|
||||
}
|
||||
|
||||
const toggleColumn = () =>
|
||||
this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click();
|
||||
|
||||
@@ -75,11 +77,13 @@ export class ToolbarFieldsPage extends BasePage {
|
||||
}
|
||||
|
||||
async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
|
||||
await this.waitForResponse({
|
||||
uiAction: () => this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(),
|
||||
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
|
||||
httpMethodsToMatch: ['GET'],
|
||||
});
|
||||
// hide field doesn't trigger an un-solicited update from backend
|
||||
// await this.waitForResponse({
|
||||
// uiAction: () => this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click(),
|
||||
// requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
|
||||
// httpMethodsToMatch: ['GET'],
|
||||
// });
|
||||
await this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('.nc-switch').click();
|
||||
await this.toolbar.parent.waitLoading();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user