Browse Source

Merge pull request #4031 from nocodb/fix/sidebar-sort

pull/4064/head
Braks 2 years ago committed by GitHub
parent
commit
16e8c65de7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      packages/nc-gui/components/dlg/ViewCreate.vue
  2. 24
      packages/nc-gui/components/smartsheet/sidebar/MenuBottom.vue
  3. 59
      packages/nc-gui/components/smartsheet/sidebar/MenuTop.vue
  4. 6
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  5. 103
      packages/nc-gui/components/smartsheet/sidebar/index.vue
  6. 3
      packages/nc-gui/components/tabs/Smartsheet.vue
  7. 13
      packages/nc-gui/composables/useDialog/index.ts
  8. 42
      packages/nc-gui/composables/useKanbanViewStore.ts
  9. 14
      packages/nc-gui/composables/useViews.ts
  10. 1
      packages/nc-gui/context/index.ts
  11. 1
      packages/nc-gui/lang/en.json

37
packages/nc-gui/components/dlg/ViewCreate.vue

@ -2,17 +2,16 @@
import type { ComponentPublicInstance } from '@vue/runtime-core'
import type { Form as AntForm, SelectProps } from 'ant-design-vue'
import { capitalize } from '@vue/runtime-core'
import type { FormType, GalleryType, GridType, KanbanType } from 'nocodb-sdk'
import type { FormType, GalleryType, GridType, KanbanType, TableType, ViewType } from 'nocodb-sdk'
import { UITypes, ViewTypes } from 'nocodb-sdk'
import {
MetaInj,
ViewListInj,
computed,
generateUniqueTitle,
inject,
message,
nextTick,
onBeforeMount,
reactive,
ref,
unref,
useApi,
useI18n,
@ -26,6 +25,8 @@ interface Props {
title?: string
selectedViewId?: string
groupingFieldColumnId?: string
views: ViewType[]
meta: TableType
}
interface Emits {
@ -41,7 +42,7 @@ interface Form {
fk_grp_col_id: string | null
}
const props = defineProps<Props>()
const { views = [], meta, selectedViewId, groupingFieldColumnId, ...props } = defineProps<Props>()
const emits = defineEmits<Emits>()
@ -55,10 +56,6 @@ const { t } = useI18n()
const { isLoading: loading, api } = useApi()
const meta = inject(MetaInj, ref())
const viewList = inject(ViewListInj)
const form = reactive<Form>({
title: props.title || '',
type: props.type,
@ -75,7 +72,7 @@ const viewNameRules = [
{
validator: (_: unknown, v: string) =>
new Promise((resolve, reject) => {
;(unref(viewList) || []).every((v1) => ((v1 as GridType | KanbanType | GalleryType).alias || v1.title) !== v)
views.every((v1) => ((v1 as GridType | KanbanType | GalleryType).alias || v1.title) !== v)
? resolve(true)
: reject(new Error(`View name should be unique`))
}),
@ -98,7 +95,7 @@ const typeAlias = computed(
}[props.type]),
)
watch(vModel, (value) => value && init())
onBeforeMount(init)
watch(
() => props.type,
@ -108,25 +105,26 @@ watch(
)
function init() {
form.title = generateUniqueTitle(capitalize(ViewTypes[props.type].toLowerCase()), viewList?.value || [], 'title')
form.title = generateUniqueTitle(capitalize(ViewTypes[props.type].toLowerCase()), views, 'title')
if (props.selectedViewId) {
form.copy_from_id = props.selectedViewId
if (selectedViewId) {
form.copy_from_id = selectedViewId
}
// preset the grouping field column
if (props.type === ViewTypes.KANBAN) {
singleSelectFieldOptions.value = meta
.value!.columns!.filter((el) => el.uidt === UITypes.SingleSelect)
.columns!.filter((el) => el.uidt === UITypes.SingleSelect)
.map((field) => {
return {
value: field.id,
label: field.title,
}
})
if (props.groupingFieldColumnId) {
if (groupingFieldColumnId) {
// take from the one from copy view
form.fk_grp_col_id = props.groupingFieldColumnId
form.fk_grp_col_id = groupingFieldColumnId
} else {
// take the first option
form.fk_grp_col_id = singleSelectFieldOptions.value?.[0]?.value as string
@ -186,7 +184,8 @@ async function onSubmit() {
<template>
<a-modal v-model:visible="vModel" class="!top-[35%]" :confirm-loading="loading" wrap-class-name="nc-modal-view-create">
<template #title>
{{ $t('general.create') }} <span class="text-capitalize">{{ typeAlias }}</span> {{ $t('objects.view') }}
{{ $t(`general.${selectedViewId ? 'duplicate' : 'create'}`) }} <span class="text-capitalize">{{ typeAlias }}</span>
{{ $t('objects.view') }}
</template>
<a-form ref="formValidator" layout="vertical" :model="form">
@ -203,7 +202,7 @@ async function onSubmit() {
v-model:value="form.fk_grp_col_id"
class="w-full nc-kanban-grouping-field-select"
:options="singleSelectFieldOptions"
:disabled="props.groupingFieldColumnId"
:disabled="groupingFieldColumnId"
placeholder="Select a Grouping Field"
not-found-content="No Single Select Field can be found. Please create one first."
/>

24
packages/nc-gui/components/smartsheet/sidebar/MenuBottom.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ViewTypes } from 'nocodb-sdk'
import { useNuxtApp, useSmartsheetStoreOrThrow, useUIPermission, viewIcons } from '#imports'
import { useNuxtApp, useSmartsheetStoreOrThrow, viewIcons } from '#imports'
interface Emits {
(event: 'openModal', data: { type: ViewTypes; title?: string }): void
@ -10,8 +10,6 @@ const emits = defineEmits<Emits>()
const { $e } = useNuxtApp()
const { isUIAllowed } = useUIPermission()
const { isSqlView } = useSmartsheetStoreOrThrow()
function onOpenModal(type: ViewTypes, title = '') {
@ -22,14 +20,14 @@ function onOpenModal(type: ViewTypes, title = '') {
<template>
<a-menu :selected-keys="[]" class="flex flex-col">
<div v-if="isUIAllowed('virtualViewsCreateOrEdit')">
<h3 class="px-3 py-1 text-xs font-semibold flex items-center gap-4 text-gray-500">
<div>
<h3 class="px-3 text-xs font-semibold flex items-center gap-4 text-gray-500">
{{ $t('activity.createView') }}
</h3>
<a-menu-item
key="grid"
class="group !flex !items-center !my-0 !h-[30px] nc-create-grid-view"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-grid-view"
@click="onOpenModal(ViewTypes.GRID)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
@ -37,7 +35,7 @@ function onOpenModal(type: ViewTypes, title = '') {
{{ $t('msg.info.addView.grid') }}
</template>
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GRID].icon" :style="{ color: viewIcons[ViewTypes.GRID].color }" />
<div>{{ $t('objects.viewType.grid') }}</div>
@ -51,7 +49,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<a-menu-item
key="gallery"
class="group !flex !items-center !my-0 nc-create-gallery-view"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-gallery-view"
@click="onOpenModal(ViewTypes.GALLERY)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
@ -59,7 +57,7 @@ function onOpenModal(type: ViewTypes, title = '') {
{{ $t('msg.info.addView.gallery') }}
</template>
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.GALLERY].icon" :style="{ color: viewIcons[ViewTypes.GALLERY].color }" />
<div>{{ $t('objects.viewType.gallery') }}</div>
@ -74,7 +72,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<a-menu-item
v-if="!isSqlView"
key="form"
class="group !flex !items-center !my-0 !h-[30px] nc-create-form-view"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-form-view"
@click="onOpenModal(ViewTypes.FORM)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
@ -82,7 +80,7 @@ function onOpenModal(type: ViewTypes, title = '') {
{{ $t('msg.info.addView.form') }}
</template>
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.FORM].icon" :style="{ color: viewIcons[ViewTypes.FORM].color }" />
<div>{{ $t('objects.viewType.form') }}</div>
@ -96,7 +94,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<a-menu-item
key="kanban"
class="group !flex !items-center !my-0 nc-create-kanban-view"
class="group !flex !items-center !my-0 !h-2.5rem nc-create-kanban-view"
@click="onOpenModal(ViewTypes.KANBAN)"
>
<a-tooltip :mouse-enter-delay="1" placement="left">
@ -104,7 +102,7 @@ function onOpenModal(type: ViewTypes, title = '') {
{{ $t('msg.info.addView.kanban') }}
</template>
<div class="nc-project-menu-item text-xs flex items-center h-full w-full gap-2">
<div class="nc-project-menu-item !py-0 text-xs flex items-center h-full w-full gap-2">
<component :is="viewIcons[ViewTypes.KANBAN].icon" :style="{ color: viewIcons[ViewTypes.KANBAN].color }" />
<div>{{ $t('objects.viewType.kanban') }}</div>

59
packages/nc-gui/components/smartsheet/sidebar/MenuTop.vue

@ -1,12 +1,11 @@
<script lang="ts" setup>
import type { ViewType, ViewTypes } from 'nocodb-sdk'
import type { ViewType } from 'nocodb-sdk'
import { ViewTypes } from 'nocodb-sdk'
import type { SortableEvent } from 'sortablejs'
import type { Menu as AntMenu } from 'ant-design-vue'
import type { Ref } from 'vue'
import Sortable from 'sortablejs'
import type { Menu as AntMenu } from 'ant-design-vue'
import {
ActiveViewInj,
ViewListInj,
extractSdkResponseErrorMsg,
inject,
message,
@ -22,9 +21,9 @@ import {
watch,
} from '#imports'
const emits = defineEmits<Emits>()
const { t } = useI18n()
interface Props {
views: ViewType[]
}
interface Emits {
(event: 'openModal', data: { type: ViewTypes; title?: string; copyViewId?: string; groupingFieldColumnId?: string }): void
@ -32,12 +31,16 @@ interface Emits {
(event: 'deleted'): void
}
const { views = [] } = defineProps<Props>()
const emits = defineEmits<Emits>()
const { t } = useI18n()
const { $e } = useNuxtApp()
const activeView = inject(ActiveViewInj, ref())
const views = inject<Ref<ViewType[]>>(ViewListInj, ref([]))
const { api } = useApi()
const router = useRouter()
@ -54,10 +57,8 @@ let isMarked = $ref<string | false>(false)
/** Watch currently active view, so we can mark it in the menu */
watch(activeView, (nextActiveView) => {
const _nextActiveView = nextActiveView as ViewType
if (_nextActiveView && _nextActiveView.id) {
selected.value = [_nextActiveView.id]
if (nextActiveView && nextActiveView.id) {
selected.value = [nextActiveView.id]
}
})
@ -75,7 +76,7 @@ function validate(view: ViewType) {
return 'View name is required'
}
if (views.value.some((v) => v.title === view.title && v.id !== view.id)) {
if (views.some((v) => v.title === view.title && v.id !== view.id)) {
return 'View name should be unique'
}
@ -93,7 +94,7 @@ async function onSortEnd(evt: SortableEvent) {
evt.preventDefault()
dragging = false
if (views.value.length < 2) return
if (views.length < 2) return
const { newIndex = 0, oldIndex = 0 } = evt
@ -104,17 +105,17 @@ async function onSortEnd(evt: SortableEvent) {
const previousEl = children[newIndex - 1]
const nextEl = children[newIndex + 1]
const currentItem = views.value.find((v) => v.id === evt.item.id)
const currentItem = views.find((v) => v.id === evt.item.id)
if (!currentItem || !currentItem.id) return
const previousItem = (previousEl ? views.value.find((v) => v.id === previousEl.id) : {}) as ViewType
const nextItem = (nextEl ? views.value.find((v) => v.id === nextEl.id) : {}) as ViewType
const previousItem = (previousEl ? views.find((v) => v.id === previousEl.id) : {}) as ViewType
const nextItem = (nextEl ? views.find((v) => v.id === nextEl.id) : {}) as ViewType
let nextOrder: number
// set new order value based on the new order of the items
if (views.value.length - 1 === newIndex) {
if (views.length - 1 === newIndex) {
nextOrder = parseFloat(String(previousItem.order)) + 1
} else if (newIndex === 0) {
nextOrder = parseFloat(String(nextItem.order)) / 2
@ -149,9 +150,10 @@ const initSortable = (el: HTMLElement) => {
onMounted(() => menuRef && initSortable(menuRef.$el))
/** Navigate to view by changing url param */
function changeView(view: { id: string; alias?: string; title?: string; type: ViewTypes }) {
function changeView(view: ViewType) {
router.push({ params: { viewTitle: view.title || '' } })
if (view.type === 1 && selected.value[0] === view.id) {
if (view.type === ViewTypes.FORM && selected.value[0] === view.id) {
// reload the page if the same form view is clicked
// router.go(0)
// fix me: router.go(0) reloads entire page. need to reload only the form view
@ -183,7 +185,7 @@ async function onRename(view: ViewType) {
}
/** Open delete modal */
function openDeleteDialog(view: Record<string, any>) {
function openDeleteDialog(view: ViewType) {
const isOpen = ref(true)
const { close } = useDialog(resolveComponent('DlgViewDelete'), {
@ -198,7 +200,7 @@ function openDeleteDialog(view: Record<string, any>) {
// return to the default view
router.replace({
params: {
viewTitle: views.value[0].title,
viewTitle: views[0].title,
},
})
}
@ -211,17 +213,12 @@ function openDeleteDialog(view: Record<string, any>) {
close(1000)
}
}
watch(views, (nextViews) => {
if (nextViews?.length && (!activeView.value || !nextViews.includes(activeView.value))) {
activeView.value = nextViews[0]
}
})
</script>
<template>
<a-menu ref="menuRef" :class="{ dragging }" class="nc-views-menu flex-1" :selected-keys="selected">
<LazySmartsheetSidebarRenameableMenuItem
<!-- Lazy load breaks menu item active styles, i.e. styles never change even when active item changes -->
<SmartsheetSidebarRenameableMenuItem
v-for="view of views"
:id="view.id"
:key="view.id"
@ -235,7 +232,7 @@ watch(views, (nextViews) => {
}"
@change-view="changeView"
@open-modal="$emit('openModal', $event)"
@delete="openDeleteDialog(view)"
@delete="openDeleteDialog"
@rename="onRename"
/>
</a-menu>

6
packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -30,7 +30,7 @@ const props = defineProps<Props>()
const emits = defineEmits<Emits>()
const vModel = useVModel(props, 'view', emits) as WritableComputedRef<ViewType & { is_default: boolean }>
const vModel = useVModel(props, 'view', emits) as WritableComputedRef<ViewType & { alias?: string; is_default: boolean }>
const { $e } = useNuxtApp()
@ -56,6 +56,8 @@ const onClick = useDebounceFn(() => {
/** Enable editing view name on dbl click */
function onDblClick() {
if (!isUIAllowed('virtualViewsCreateOrEdit')) return
if (!isEditing) {
isEditing = true
originalTitle = vModel.value.title
@ -162,7 +164,7 @@ function onStopEdit() {
<template>
<a-menu-item
class="select-none group !flex !items-center !my-0 hover:(bg-primary !bg-opacity-5)"
@dblclick.stop="isUIAllowed('virtualViewsCreateOrEdit') && onDblClick()"
@dblclick.stop="onDblClick"
@click.stop="onClick"
>
<div v-e="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2">

103
packages/nc-gui/components/smartsheet/sidebar/index.vue

@ -3,11 +3,11 @@ import type { ViewType, ViewTypes } from 'nocodb-sdk'
import {
ActiveViewInj,
MetaInj,
ViewListInj,
computed,
inject,
provide,
ref,
resolveComponent,
useDialog,
useNuxtApp,
useRoute,
useRouter,
@ -21,7 +21,7 @@ const meta = inject(MetaInj, ref())
const activeView = inject(ActiveViewInj, ref())
const { views, loadViews } = useViews(meta)
const { views, loadViews, isLoading } = useViews(meta)
const { isUIAllowed } = useUIPermission()
@ -31,8 +31,6 @@ const route = useRoute()
const { $e } = useNuxtApp()
provide(ViewListInj, views)
/** Sidebar visible */
const { isOpen } = useSidebar('nc-right-sidebar')
@ -41,21 +39,6 @@ const sidebarCollapsed = computed(() => !isOpen.value)
/** Sidebar ref */
const sidebar = ref()
/** View type to create from modal */
let viewCreateType = $ref<ViewTypes>()
/** View title to create from modal (when duplicating) */
let viewCreateTitle = $ref('')
/** selected view id for copying view meta */
let selectedViewId = $ref('')
/** Kanban Grouping Column Id for copying view meta */
let kanbanGrpColumnId = $ref('')
/** is view creation modal open */
let modalOpen = $ref(false)
/** Watch route param and change active view based on `viewTitle` */
watch(
[views, () => route.params.viewTitle],
@ -80,39 +63,54 @@ watch(
activeView.value = nextViews[0]
}
}
/** if active view is not found, set it to first view */
if (!activeView.value && nextViews.length) {
if (nextViews?.length && (!activeView.value || !nextViews.includes(activeView.value))) {
activeView.value = nextViews[0]
}
},
{ immediate: true },
)
/** Open view creation modal */
function openModal({
type,
/** Open delete modal */
function onOpenModal({
title = '',
type,
copyViewId,
groupingFieldColumnId,
}: {
title?: string
type: ViewTypes
title: string
copyViewId: string
groupingFieldColumnId: string
copyViewId?: string
groupingFieldColumnId?: string
}) {
modalOpen = true
viewCreateType = type
viewCreateTitle = title
selectedViewId = copyViewId
kanbanGrpColumnId = groupingFieldColumnId
}
const isOpen = ref(true)
const { close } = useDialog(resolveComponent('DlgViewCreate'), {
'modelValue': isOpen,
title,
type,
meta,
'selectedViewId': copyViewId,
groupingFieldColumnId,
'views': views,
'onUpdate:modelValue': closeDialog,
'onCreated': async (view: ViewType) => {
closeDialog()
await loadViews()
router.push({ params: { viewTitle: view.title || '' } })
$e('a:view:create', { view: view.type })
},
})
/** Handle view creation */
async function onCreate(view: ViewType) {
await loadViews()
router.push({ params: { viewTitle: view.title || '' } })
modalOpen = false
$e('a:view:create', { view: view.type })
function closeDialog() {
isOpen.value = false
close(1000)
}
}
</script>
@ -127,27 +125,24 @@ async function onCreate(view: ViewType) {
theme="light"
>
<LazySmartsheetSidebarToolbar
v-if="isOpen"
class="min-h-[var(--toolbar-height)] max-h-[var(--toolbar-height)] flex items-center py-3 px-3 justify-between border-b-1"
/>
<div v-if="isOpen" class="flex-1 flex flex-col min-h-0">
<LazySmartsheetSidebarMenuTop @open-modal="openModal" @deleted="loadViews" />
<div class="flex-1 flex flex-col min-h-0">
<GeneralOverlay v-if="!views.length" :model-value="isLoading" inline class="bg-gray-300/50">
<div class="w-full h-full flex items-center justify-center">
<a-spin />
</div>
</GeneralOverlay>
<div v-if="isUIAllowed('virtualViewsCreateOrEdit')" class="!my-3 w-full border-b-1" />
<LazySmartsheetSidebarMenuTop :views="views" @open-modal="onOpenModal" @deleted="loadViews" />
<LazySmartsheetSidebarMenuBottom @open-modal="openModal" />
</div>
<template v-if="isUIAllowed('virtualViewsCreateOrEdit')">
<div class="!my-3 w-full border-b-1" />
<LazyDlgViewCreate
v-if="views"
v-model="modalOpen"
:title="viewCreateTitle"
:type="viewCreateType"
:selected-view-id="selectedViewId"
:grouping-field-column-id="kanbanGrpColumnId"
@created="onCreate"
/>
<LazySmartsheetSidebarMenuBottom @open-modal="onOpenModal" />
</template>
</div>
</a-layout-sider>
</template>

3
packages/nc-gui/components/tabs/Smartsheet.vue

@ -79,7 +79,8 @@ provide(TabMetaInj, activeTab)
</Transition>
</div>
<LazySmartsheetSidebar v-if="meta" class="nc-right-sidebar" />
<!-- Lazy loading the sidebar causes issues when deleting elements, i.e. it appears as if multiple elements are removed when they are not -->
<SmartsheetSidebar v-if="meta" class="nc-right-sidebar" />
</div>
</template>

13
packages/nc-gui/composables/useDialog/index.ts

@ -1,8 +1,9 @@
import type { VNode } from '@vue/runtime-dom'
import { isVNode, render } from '@vue/runtime-dom'
import type { ComponentPublicInstance } from '@vue/runtime-core'
import type { MaybeRef } from '@vueuse/core'
import { isClient } from '@vueuse/core'
import { createEventHook, h, ref, toReactive, tryOnScopeDispose, useNuxtApp, watch } from '#imports'
import { createEventHook, h, ref, toReactive, tryOnScopeDispose, unref, useNuxtApp, watch } from '#imports'
/**
* Programmatically create a component and attach it to the body (or a specific mount target), like a dialog or modal.
@ -38,7 +39,7 @@ import { createEventHook, h, ref, toReactive, tryOnScopeDispose, useNuxtApp, wat
export function useDialog(
componentOrVNode: any,
props: NonNullable<Parameters<typeof h>[1]> = {},
mountTarget?: Element | ComponentPublicInstance,
mountTarget?: MaybeRef<Element | ComponentPublicInstance>,
) {
if (typeof document === 'undefined' || !isClient) {
console.warn('[useDialog]: Cannot use outside of browser!')
@ -53,10 +54,12 @@ export function useDialog(
const vNodeRef = ref<VNode>()
mountTarget = mountTarget ? ('$el' in mountTarget ? (mountTarget.$el as HTMLElement) : mountTarget) : document.body
let _mountTarget = unref(mountTarget)
_mountTarget = _mountTarget ? ('$el' in _mountTarget ? (_mountTarget.$el as HTMLElement) : _mountTarget) : document.body
/** if specified, append vnode to mount target instead of document.body */
mountTarget.appendChild(domNode)
_mountTarget.appendChild(domNode)
/** When props change, we want to re-render the element with the new prop values */
const stop = watch(
@ -87,7 +90,7 @@ export function useDialog(
setTimeout(() => {
try {
;(mountTarget as HTMLElement)?.removeChild(domNode)
;(_mountTarget as HTMLElement)?.removeChild(domNode)
} catch (e) {}
}, 100)

42
packages/nc-gui/composables/useKanbanViewStore.ts

@ -1,9 +1,27 @@
import type { ComputedRef, Ref } from 'vue'
import type { Api, ColumnType, KanbanType, SelectOptionType, SelectOptionsType, TableType, ViewType } from 'nocodb-sdk'
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue'
import type { Row } from '~/lib'
import { SharedViewPasswordInj, deepCompare, enumColor, extractPkFromRow, useInjectionState, useNuxtApp } from '#imports'
import {
IsPublicInj,
SharedViewPasswordInj,
deepCompare,
enumColor,
extractPkFromRow,
extractSdkResponseErrorMsg,
getHTMLEncodedText,
inject,
message,
provide,
ref,
useApi,
useI18n,
useInjectionState,
useNuxtApp,
useProject,
useSharedView,
useSmartsheetStoreOrThrow,
useUIPermission,
} from '#imports'
type GroupingFieldColOptionsType = SelectOptionType & { collapsed: boolean }
@ -223,7 +241,7 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
groupingFieldColOptions.value = [
...((groupingFieldColumn.value?.colOptions as SelectOptionsType & { collapsed: boolean })?.options ?? []),
// enrich uncategorized stack
{ id: 'uncategorized', title: null, order: 0, color: enumColor.light[2] },
{ id: 'uncategorized', title: null, order: 0, color: enumColor.light[2] } as any,
]
// sort by initial order
.sort((a, b) => a.order! - b.order!)
@ -302,15 +320,13 @@ const [useProvideKanbanViewStore, useKanbanViewStore] = useInjectionState(
// }
)
// audit
$api.utils
.auditRowUpdate(id, {
fk_model_id: meta.value?.id as string,
column_name: property,
row_id: id,
value: getHTMLEncodedText(toUpdate.row[property]),
prev_value: getHTMLEncodedText(toUpdate.oldRow[property]),
})
.then(() => {})
$api.utils.auditRowUpdate(id, {
fk_model_id: meta.value?.id as string,
column_name: property,
row_id: id,
value: getHTMLEncodedText(toUpdate.row[property]),
prev_value: getHTMLEncodedText(toUpdate.oldRow[property]),
})
/** update row data(to sync formula and other related columns) */
Object.assign(toUpdate.row, updatedRowData)

14
packages/nc-gui/composables/useViews.ts

@ -1,24 +1,28 @@
import type { TableType, ViewType } from 'nocodb-sdk'
import type { MaybeRef } from '@vueuse/core'
import { unref, useNuxtApp, watch } from '#imports'
import { ref, unref, useNuxtApp, watch } from '#imports'
export function useViews(meta: MaybeRef<TableType | undefined>) {
let views = $ref<ViewType[]>([])
const views = ref<ViewType[]>([])
const isLoading = ref(false)
const { $api } = useNuxtApp()
const loadViews = async () => {
isLoading.value = true
const _meta = unref(meta)
if (_meta && _meta.id) {
const response = (await $api.dbView.list(_meta.id)).list as ViewType[]
if (response) {
views = response.sort((a, b) => a.order! - b.order!)
views.value = response.sort((a, b) => a.order! - b.order!)
}
}
isLoading.value = false
}
watch(meta, loadViews, { immediate: true })
watch(() => unref(meta), loadViews, { immediate: true })
return { views: $$(views), loadViews }
return { views, isLoading, loadViews }
}

1
packages/nc-gui/context/index.ts

@ -27,7 +27,6 @@ export const ReloadViewMetaHookInj: InjectionKey<EventHook<boolean | void>> = Sy
export const ReloadRowDataHookInj: InjectionKey<EventHook<boolean | void>> = Symbol('reload-row-data-injection')
export const OpenNewRecordFormHookInj: InjectionKey<EventHook<void>> = Symbol('open-new-record-form-injection')
export const FieldsInj: InjectionKey<Ref<any[]>> = Symbol('fields-injection')
export const ViewListInj: InjectionKey<Ref<ViewType[]>> = Symbol('view-list-injection')
export const EditModeInj: InjectionKey<Ref<boolean>> = Symbol('edit-mode-injection')
export const SharedViewPasswordInj: InjectionKey<Ref<string | null>> = Symbol('shared-view-password-injection')
export const CellUrlDisableOverlayInj: InjectionKey<Ref<boolean>> = Symbol('cell-url-disable-url')

1
packages/nc-gui/lang/en.json

@ -16,6 +16,7 @@
"cancel": "Cancel",
"submit": "Submit",
"create": "Create",
"duplicate": "Duplicate",
"insert": "Insert",
"delete": "Delete",
"update": "Update",

Loading…
Cancel
Save