Browse Source

Merge pull request #6554 from nocodb/nc-fix/exp-modal

Fix: Expanded Records
pull/6559/head
Anbarasu 1 year ago committed by GitHub
parent
commit
7f65cd6650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      packages/nc-gui/components/smartsheet/Gallery.vue
  2. 1
      packages/nc-gui/components/smartsheet/Map.vue
  3. 12
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  4. 93
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  5. 1
      packages/nc-gui/components/smartsheet/grid/index.vue
  6. 6
      packages/nc-gui/components/smartsheet/header/Cell.vue
  7. 14
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  8. 1
      packages/nc-gui/components/tabs/Smartsheet.vue
  9. 3
      packages/nc-gui/composables/useExpandedFormStore.ts
  10. 12
      packages/nc-gui/utils/dataUtils.ts
  11. 3
      packages/nocodb/src/services/audits.service.ts

3
packages/nc-gui/components/smartsheet/Gallery.vue

@ -126,7 +126,7 @@ const attachments = (record: any): Attachment[] => {
const expandForm = (row: RowType, state?: Record<string, any>) => { const expandForm = (row: RowType, state?: Record<string, any>) => {
const rowId = extractPkFromRow(row.row, meta.value!.columns!) const rowId = extractPkFromRow(row.row, meta.value!.columns!)
if (rowId && !isPublic.value) { if (rowId) {
router.push({ router.push({
query: { query: {
...route.query, ...route.query,
@ -374,7 +374,6 @@ watch(
<Suspense> <Suspense>
<LazySmartsheetExpandedForm <LazySmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg" v-if="expandedFormOnRowIdDlg"
:key="route.query.rowId"
v-model="expandedFormOnRowIdDlg" v-model="expandedFormOnRowIdDlg"
:row="{ row: {}, oldRow: {}, rowMeta: {} }" :row="{ row: {}, oldRow: {}, rowMeta: {} }"
:meta="meta" :meta="meta"

1
packages/nc-gui/components/smartsheet/Map.vue

@ -246,7 +246,6 @@ const count = computed(() => paginationData.value.totalRows)
<Suspense v-if="!isPublic"> <Suspense v-if="!isPublic">
<LazySmartsheetExpandedForm <LazySmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg" v-if="expandedFormOnRowIdDlg"
:key="route.query.rowId"
v-model="expandedFormOnRowIdDlg" v-model="expandedFormOnRowIdDlg"
:row="{ row: {}, oldRow: {}, rowMeta: {} }" :row="{ row: {}, oldRow: {}, rowMeta: {} }"
:meta="meta" :meta="meta"

12
packages/nc-gui/components/smartsheet/expanded-form/Comments.vue

@ -18,8 +18,6 @@ const tab = ref<'comments' | 'audits'>('comments')
const { isUIAllowed } = useRoles() const { isUIAllowed } = useRoles()
const { appInfo } = useGlobal()
const hasEditPermission = computed(() => isUIAllowed('commentEdit')) const hasEditPermission = computed(() => isUIAllowed('commentEdit'))
const editLog = ref<AuditType>() const editLog = ref<AuditType>()
@ -169,13 +167,13 @@ const processedAudit = (log: string) => {
<span class="truncate font-bold max-w-42"> <span class="truncate font-bold max-w-42">
{{ log.display_name ?? log.user.split('@')[0] ?? 'Shared source' }} {{ log.display_name ?? log.user.split('@')[0] ?? 'Shared source' }}
</span> </span>
<div v-if="log.id !== editLog?.id" class="text-xs text-gray-500"> <div v-if="log.id !== editLog?.id" class="text-xs font-medium text-gray-500">
{{ log.created_at !== log.updated_at ? `Edited ${timeAgo(log.updated_at)}` : timeAgo(log.created_at) }} {{ log.created_at !== log.updated_at ? `Edited ${timeAgo(log.updated_at)}` : timeAgo(log.created_at) }}
</div> </div>
</div> </div>
</div> </div>
<NcButton <NcButton
v-if="log.user === user!.email && !editLog && !appInfo.ee" v-if="log.user === user!.email && !editLog"
v-e="['c:row-expand:comment:edit']" v-e="['c:row-expand:comment:edit']"
type="secondary" type="secondary"
class="!px-2 opacity-0 group-hover:opacity-100 transition-all" class="!px-2 opacity-0 group-hover:opacity-100 transition-all"
@ -240,14 +238,14 @@ const processedAudit = (log: string) => {
<div class="bg-white rounded-xl border-1 gap-3 border-gray-200"> <div class="bg-white rounded-xl border-1 gap-3 border-gray-200">
<div class="flex flex-col p-4 gap-3"> <div class="flex flex-col p-4 gap-3">
<div class="flex justify-between"> <div class="flex justify-between">
<div class="flex font-bold items-center gap-2"> <div class="flex items-center gap-2">
<GeneralUserIcon size="base" :name="log.display_name ?? log.user" /> <GeneralUserIcon size="base" :name="log.display_name ?? log.user" />
<div class="flex flex-col"> <div class="flex flex-col">
<span class="truncate max-w-50"> <span class="truncate font-bold max-w-50">
{{ log.display_name ?? log.user.split('@')[0].slice(0, 2) ?? 'Shared source' }} {{ log.display_name ?? log.user.split('@')[0].slice(0, 2) ?? 'Shared source' }}
</span> </span>
<div v-if="log.id !== editLog?.id" class="text-xs text-gray-500"> <div v-if="log.id !== editLog?.id" class="text-xs font-medium text-gray-500">
{{ timeAgo(log.created_at) }} {{ timeAgo(log.created_at) }}
</div> </div>
</div> </div>

93
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -29,15 +29,14 @@ import {
useVModel, useVModel,
watch, watch,
} from '#imports' } from '#imports'
import type { Row } from '#imports'
interface Props { interface Props {
modelValue?: boolean modelValue?: boolean
row: Row
state?: Record<string, any> | null state?: Record<string, any> | null
meta: TableType meta: TableType
loadRow?: boolean loadRow?: boolean
useMetaFields?: boolean useMetaFields?: boolean
row?: Row
rowId?: string rowId?: string
view?: ViewType view?: ViewType
showNextPrevIcons?: boolean showNextPrevIcons?: boolean
@ -57,7 +56,9 @@ const { isMobileMode } = useGlobal()
const { t } = useI18n() const { t } = useI18n()
const row = ref(props.row) const rowId = toRef(props, 'rowId')
const row = toRef(props, 'row')
const state = toRef(props, 'state') const state = toRef(props, 'state')
@ -108,19 +109,21 @@ const {
loadRow: _loadRow, loadRow: _loadRow,
primaryKey, primaryKey,
saveRowAndStay, saveRowAndStay,
row: _row,
syncLTARRefs, syncLTARRefs,
save: _save, save: _save,
loadCommentsAndLogs,
} = useProvideExpandedFormStore(meta, row) } = useProvideExpandedFormStore(meta, row)
const duplicatingRowInProgress = ref(false) const duplicatingRowInProgress = ref(false)
if (props.loadRow) { if (props.loadRow) {
await _loadRow() await _loadRow(rowId.value)
} }
if (props.rowId) { if (rowId.value) {
try { try {
await _loadRow(props.rowId) await _loadRow(rowId.value)
} catch (e: any) { } catch (e: any) {
if (e.response?.status === 404) { if (e.response?.status === 404) {
// todo: i18n // todo: i18n
@ -149,14 +152,14 @@ const isExpanded = useVModel(props, 'modelValue', emits, {
}) })
const onClose = () => { const onClose = () => {
if (row.value?.rowMeta?.new) emits('cancel') if (_row.value?.rowMeta?.new) emits('cancel')
isExpanded.value = false isExpanded.value = false
} }
const onDuplicateRow = () => { const onDuplicateRow = () => {
duplicatingRowInProgress.value = true duplicatingRowInProgress.value = true
isUnsavedFormExist.value = true isUnsavedFormExist.value = true
const oldRow = { ...row.value.row } const oldRow = { ..._row.value.row }
delete oldRow.ncRecordId delete oldRow.ncRecordId
const newRow = Object.assign( const newRow = Object.assign(
{}, {},
@ -167,7 +170,7 @@ const onDuplicateRow = () => {
}, },
) )
setTimeout(async () => { setTimeout(async () => {
row.value = newRow _row.value = newRow
duplicatingRowInProgress.value = false duplicatingRowInProgress.value = false
message.success(t('msg.success.rowDuplicatedWithoutSavedYet')) message.success(t('msg.success.rowDuplicatedWithoutSavedYet'))
}, 500) }, 500)
@ -219,17 +222,12 @@ provide(ReloadRowDataHookInj, reloadHook)
if (isKanban.value) { if (isKanban.value) {
// adding column titles to changedColumns if they are preset // adding column titles to changedColumns if they are preset
for (const [k, v] of Object.entries(row.value.row)) { for (const [k, v] of Object.entries(_row.value.row)) {
if (v) { if (v) {
changedColumns.value.add(k) changedColumns.value.add(k)
} }
} }
} }
watch(isUnsavedFormExist, () => {
console.log(isUnsavedFormExist.value, 'HEHEH')
})
provide(IsExpandedFormOpenInj, isExpanded) provide(IsExpandedFormOpenInj, isExpanded)
const cellWrapperEl = ref() const cellWrapperEl = ref()
@ -242,7 +240,7 @@ onMounted(() => {
const addNewRow = () => { const addNewRow = () => {
setTimeout(async () => { setTimeout(async () => {
row.value = { _row.value = {
row: {}, row: {},
oldRow: {}, oldRow: {},
rowMeta: { new: true }, rowMeta: { new: true },
@ -340,22 +338,10 @@ const onConfirmDeleteRowClick = async () => {
showDeleteRowModal.value = false showDeleteRowModal.value = false
} }
watch( watch(rowId, async (nRow) => {
state, await _loadRow(nRow)
() => { await loadCommentsAndLogs()
if (!state.value?.id) return })
setTimeout(() => {
const rowDom = wrapper.value?.querySelector(`.nc-expanded-form-row[col-id="${state.value?.id}"]`)
if (rowDom) {
rowDom.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
}, 650)
},
{
immediate: true,
},
)
const showRightSections = computed(() => { const showRightSections = computed(() => {
return !isNew.value && commentsDrawer.value && isUIAllowed('commentList') return !isNew.value && commentsDrawer.value && isUIAllowed('commentList')
@ -370,7 +356,6 @@ export default {
<template> <template>
<NcModal <NcModal
:key="key"
v-model:visible="isExpanded" v-model:visible="isExpanded"
:footer="null" :footer="null"
:width="commentsDrawer && isUIAllowed('commentList') ? 'min(80vw,1280px)' : 'min(80vw,1280px)'" :width="commentsDrawer && isUIAllowed('commentList') ? 'min(80vw,1280px)' : 'min(80vw,1280px)'"
@ -404,13 +389,15 @@ export default {
<MdiChevronDown class="text-md" /> <MdiChevronDown class="text-md" />
</NcButton> </NcButton>
</div> </div>
<div v-if="displayValue" class="flex items-center truncate font-bold text-gray-800 text-xl"> <div
{{ displayValue }} v-if="displayValue && !row.rowMeta?.new"
</div> class="flex items-center truncate w-32 hover:w-64 transition-all font-bold text-gray-800 text-xl"
<div class="bg-gray-100 px-2 gap-1 flex my-1 items-center rounded-lg text-gray-800 font-medium"> >
<TableIcon class="w-6 h-6 text-sm" /> <span class="truncate">
All {{ meta.title }} {{ displayValue }}
</span>
</div> </div>
<div v-if="row.rowMeta?.new" class="flex items-center truncate font-bold text-gray-800 text-xl">New Record</div>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<NcDropdown v-if="!isNew"> <NcDropdown v-if="!isNew">
@ -509,22 +496,26 @@ export default {
:data-testid="`nc-expand-col-${col.title}`" :data-testid="`nc-expand-col-${col.title}`"
> >
<div class="flex items-start flex-row xs:(flex-col w-full) nc-expanded-cell"> <div class="flex items-start flex-row xs:(flex-col w-full) nc-expanded-cell">
<div class="w-[12rem] xs:(w-full) mt-1.5"> <div class="w-[12rem] xs:(w-full) mt-1.5 !h-[35px]">
<LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" class="nc-expanded-cell-header" :column="col" /> <LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(col)"
class="nc-expanded-cell-header !text-gray-600"
:column="col"
/>
<LazySmartsheetHeaderCell v-else class="nc-expanded-cell-header" :column="col" /> <LazySmartsheetHeaderCell v-else class="nc-expanded-cell-header !text-gray-600" :column="col" />
</div> </div>
<LazySmartsheetDivDataCell <LazySmartsheetDivDataCell
v-if="col.title" v-if="col.title"
:ref="i ? null : (el: any) => (cellWrapperEl = el)" :ref="i ? null : (el: any) => (cellWrapperEl = el)"
class="!bg-white rounded-lg !w-[20rem] !xs:w-full border-1 border-gray-200 px-1 min-h-[35px] flex items-center relative" class="!bg-white rounded-lg !w-[20rem] !xs:w-full border-1 overflow-hidden border-gray-200 px-1 min-h-[35px] flex items-center relative"
> >
<LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="row.row[col.title]" :row="row" :column="col" /> <LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="_row.row[col.title]" :row="_row" :column="col" />
<LazySmartsheetCell <LazySmartsheetCell
v-else v-else
v-model="row.row[col.title]" v-model="_row.row[col.title]"
:column="col" :column="col"
:edit-enabled="true" :edit-enabled="true"
:active="true" :active="true"
@ -553,22 +544,22 @@ export default {
:data-testid="`nc-expand-col-${col.title}`" :data-testid="`nc-expand-col-${col.title}`"
> >
<div class="flex flex-row items-start"> <div class="flex flex-row items-start">
<div class="w-[12rem] scale-110 mt-2.5"> <div class="w-[12rem] scale-110 !h-[35px] mt-2.5">
<LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" /> <LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" class="!text-gray-600" :column="col" />
<LazySmartsheetHeaderCell v-else :column="col" /> <LazySmartsheetHeaderCell v-else class="!text-gray-600" :column="col" />
</div> </div>
<LazySmartsheetDivDataCell <LazySmartsheetDivDataCell
v-if="col.title" v-if="col.title"
:ref="i ? null : (el: any) => (cellWrapperEl = el)" :ref="i ? null : (el: any) => (cellWrapperEl = el)"
class="!bg-white rounded-lg !w-[20rem] border-1 border-gray-200 px-1 min-h-[35px] flex items-center relative" class="!bg-white rounded-lg !w-[20rem] border-1 overflow-hidden border-gray-200 px-1 min-h-[35px] flex items-center relative"
> >
<LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="row.row[col.title]" :row="row" :column="col" /> <LazySmartsheetVirtualCell v-if="isVirtualCol(col)" v-model="_row.row[col.title]" :row="_row" :column="col" />
<LazySmartsheetCell <LazySmartsheetCell
v-else v-else
v-model="row.row[col.title]" v-model="_row.row[col.title]"
:column="col" :column="col"
:edit-enabled="true" :edit-enabled="true"
:active="true" :active="true"

1
packages/nc-gui/components/smartsheet/grid/index.vue

@ -241,7 +241,6 @@ onMounted(() => {
<SmartsheetExpandedForm <SmartsheetExpandedForm
v-if="expandedFormOnRowIdDlg" v-if="expandedFormOnRowIdDlg"
:key="routeQuery.rowId"
v-model="expandedFormOnRowIdDlg" v-model="expandedFormOnRowIdDlg"
:row="{ row: {}, oldRow: {}, rowMeta: {} }" :row="{ row: {}, oldRow: {}, rowMeta: {} }"
:meta="meta" :meta="meta"

6
packages/nc-gui/components/smartsheet/header/Cell.vue

@ -74,7 +74,11 @@ const openDropDown = () => {
<div <div
v-if="column" v-if="column"
class="name pl-1" class="name pl-1"
:class="{ 'cursor-pointer pt-0.25': !isForm && isUIAllowed('fieldEdit') && !hideMenu }" :class="{
'cursor-pointer pt-0.25': !isForm && isUIAllowed('fieldEdit') && !hideMenu && !isExpandedForm,
'cursor-default': isForm || !isUIAllowed('fieldEdit') || hideMenu,
'!truncate': !isForm,
}"
:data-test-id="column.title" :data-test-id="column.title"
> >
{{ column.title }} {{ column.title }}

14
packages/nc-gui/components/smartsheet/header/VirtualCell.vue

@ -123,22 +123,14 @@ const closeAddColumnDropdown = () => {
</script> </script>
<template> <template>
<div <div class="flex items-center w-full text-xs text-gray-500 font-weight-medium" @click.right="isDropDownOpen = !isDropDownOpen">
class="flex items-center w-full text-xs text-gray-500 font-weight-medium"
:class="{ 'h-full': column }"
@click.right="isDropDownOpen = !isDropDownOpen"
>
<LazySmartsheetHeaderVirtualCellIcon v-if="column && !props.hideIcon" /> <LazySmartsheetHeaderVirtualCellIcon v-if="column && !props.hideIcon" />
<a-tooltip placement="bottom"> <a-tooltip placement="bottom">
<template #title> <template v-if="!isForm && !isExpandedForm" #title>
{{ tooltipMsg }} {{ tooltipMsg }}
</template> </template>
<span <span class="name truncate pl-1" :class="{ truncate: !isForm }" :data-test-id="column.title">
class="name pl-1"
:class="{ 'truncate': !isForm || !isExpandedForm, 'whitespace-pre-line': isForm || isExpandedForm }"
:data-test-id="column.title"
>
{{ column.title }} {{ column.title }}
</span> </span>
</a-tooltip> </a-tooltip>

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

@ -190,7 +190,6 @@ watch([activeViewTitleOrId, activeTableId], () => {
</div> </div>
<SmartsheetDetails v-else /> <SmartsheetDetails v-else />
</div> </div>
<LazySmartsheetExpandedFormDetached /> <LazySmartsheetExpandedFormDetached />
</div> </div>
</template> </template>

3
packages/nc-gui/composables/useExpandedFormStore.ts

@ -24,7 +24,7 @@ import {
} from '#imports' } from '#imports'
import type { Row } from '#imports' import type { Row } from '#imports'
const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((meta: Ref<TableType>, row: Ref<Row>) => { const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((meta: Ref<TableType>, _row: Ref<Row>) => {
const { $e, $state, $api } = useNuxtApp() const { $e, $state, $api } = useNuxtApp()
const { api, isLoading: isCommentsLoading, error: commentsError } = useApi() const { api, isLoading: isCommentsLoading, error: commentsError } = useApi()
@ -44,6 +44,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
const changedColumns = ref(new Set<string>()) const changedColumns = ref(new Set<string>())
const { base } = storeToRefs(useBase()) const { base } = storeToRefs(useBase())
const row = ref<Row>(_row.value.rowMeta.new ? _row.value : ({ row: {}, oldRow: {}, rowMeta: {} } as Row))
const rowStore = useProvideSmartsheetRowStore(meta, row) const rowStore = useProvideSmartsheetRowStore(meta, row)

12
packages/nc-gui/utils/dataUtils.ts

@ -4,13 +4,11 @@ import type { Row } from 'lib'
import { isColumnRequiredAndNull } from './columnUtils' import { isColumnRequiredAndNull } from './columnUtils'
export const extractPkFromRow = (row: Record<string, any>, columns: ColumnType[]) => { export const extractPkFromRow = (row: Record<string, any>, columns: ColumnType[]) => {
return ( if (!row || !columns) return null
row && return columns
columns .filter((c) => c.pk)
?.filter((c) => c.pk) .map((c) => row?.[c.title as string])
.map((c) => row?.[c.title as string]) .join('___')
.join('___')
)
} }
export const rowPkData = (row: Record<string, any>, columns: ColumnType[]) => { export const rowPkData = (row: Record<string, any>, columns: ColumnType[]) => {

3
packages/nocodb/src/services/audits.service.ts

@ -93,6 +93,9 @@ export class AuditsService {
); );
const log = await Audit.get(param.auditId); const log = await Audit.get(param.auditId);
if (log.op_type !== AuditOperationTypes.COMMENT) {
NcError.forbidden('Only comments can be updated');
}
if (log.user !== param.userEmail) { if (log.user !== param.userEmail) {
NcError.unauthorized('Unauthorized access'); NcError.unauthorized('Unauthorized access');

Loading…
Cancel
Save