Browse Source

Merge pull request #6558 from nocodb/nc-fix/mobile-links-modal

Mobile links modal
pull/6566/head
Muhammed Mustafa 1 year ago committed by GitHub
parent
commit
b36af1498b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui/components/nc/Modal.vue
  2. 9
      packages/nc-gui/components/nc/Pagination.vue
  3. 4
      packages/nc-gui/components/smartsheet/Gallery.vue
  4. 2
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  5. 66
      packages/nc-gui/components/virtual-cell/components/Header.vue
  6. 197
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  7. 43
      packages/nc-gui/components/virtual-cell/components/ListItem.vue
  8. 170
      packages/nc-gui/components/virtual-cell/components/ListItems.vue
  9. 1
      packages/nc-gui/lang/en.json

2
packages/nc-gui/components/nc/Modal.vue

@ -83,7 +83,7 @@ const slots = useSlots()
@keydown.esc="visible = false"
>
<div
class="flex flex-col nc-modal p-6"
class="flex flex-col nc-modal p-6 h-full"
:style="{
maxHeight: height,
}"

9
packages/nc-gui/components/nc/Pagination.vue

@ -4,6 +4,7 @@ const props = defineProps<{
total: number
pageSize: number
entityName?: string
mode: 'simple' | 'full'
}>()
const emits = defineEmits(['update:current', 'update:pageSize'])
@ -20,6 +21,8 @@ const totalPages = computed(() => Math.max(Math.ceil(total.value / pageSize.valu
const { isMobileMode } = useGlobal()
const mode = computed(() => props.mode || (isMobileMode.value ? 'simple' : 'full'))
const changePage = ({ increase }: { increase: boolean }) => {
if (increase && current.value < totalPages.value) {
current.value = current.value + 1
@ -40,7 +43,7 @@ const goToFirstPage = () => {
<template>
<div class="nc-pagination flex flex-row items-center gap-x-2">
<NcButton
v-if="!isMobileMode"
v-if="mode === 'full'"
v-e="[`a:pagination:${entityName}:first-page`]"
class="first-page"
type="secondary"
@ -63,7 +66,7 @@ const goToFirstPage = () => {
</NcButton>
<div class="text-gray-600">
<span class="active"> {{ current }} </span>
<span class="mx-1"> {{ isMobileMode ? '/' : 'of' }} </span>
<span class="mx-1"> {{ mode !== 'full' ? '/' : 'of' }} </span>
<span class="total">
{{ totalPages }}
</span>
@ -81,7 +84,7 @@ const goToFirstPage = () => {
</NcButton>
<NcButton
v-if="!isMobileMode"
v-if="mode === 'full'"
v-e="[`a:pagination:${entityName}:last-page`]"
class="last-page"
type="secondary"

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

@ -347,9 +347,7 @@ watch(
:read-only="true"
/>
</div>
<div v-else class="flex flex-row w-full h-[1.375rem] pl-1 items-center justify-start">
<span class="bg-gray-200 h-2 w-16 rounded-md"></span>
</div>
<div v-else class="flex flex-row w-full h-[1.375rem] pl-1 items-center justify-start">-</div>
</div>
</div>
</a-card>

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

@ -464,7 +464,7 @@ export default {
<div>{{ meta.title }}</div>
</div>
<NcButton
v-if="!props.lastRow"
v-if="props.showNextPrevIcons && !props.lastRow"
v-e="['c:row-expand:next']"
type="secondary"
class="nc-next-arrow !w-10"

66
packages/nc-gui/components/virtual-cell/components/Header.vue

@ -13,6 +13,8 @@ const { relation, relatedTableTitle, displayValue, showHeader, tableTitle } = de
displayValue?: string
}>()
const { isMobileMode } = useGlobal()
const { t } = useI18n()
const relationMeta = computed(() => {
@ -50,17 +52,19 @@ const relationMeta = computed(() => {
</script>
<template>
<div class="flex justify-between relative pb-2 items-center">
<span class="text-base font-bold flex mt-2 justify-center">
<div class="flex sm:justify-between relative pb-2 items-center">
<div v-if="!isMobileMode" class="flex text-base font-bold justify-start items-center min-w-36">
{{ showHeader ? 'Linked Records' : '' }}
</span>
<div class="grid grid-cols-[1fr,auto,1fr] justify-center items-center gap-2 absolute inset-0 m-auto">
<div class="flex justify-end">
<div class="flex flex-shrink-0 rounded-md gap-1 text-brand-500 items-center bg-gray-100 px-2 py-1">
<FileIcon class="w-4 h-4" />
<GeneralTruncateText placement="top" length="25">
</div>
<div class="flex flex-row sm:w-[calc(100%-16rem)] xs:w-full items-center justify-center gap-2 xs:(h-full)">
<div class="flex sm:justify-end w-[calc(50%-1.5rem)] xs:(w-[calc(50%-1.5rem)] h-full)">
<div
class="flex max-w-full xs:w-full flex-shrink-0 xs:(h-full) rounded-md gap-1 text-brand-500 items-center bg-gray-100 px-2 py-1"
>
<FileIcon class="w-4 h-4 min-w-4" />
<span class="truncate">
{{ displayValue }}
</GeneralTruncateText>
</span>
</div>
</div>
<NcTooltip class="flex-shrink-0">
@ -75,9 +79,9 @@ const relationMeta = computed(() => {
}"
/>
</NcTooltip>
<div class="flex justify-start">
<div class="flex justify-start xs:w-[calc(50%-1.5rem)] w-[calc(50%-1.5rem)] xs:justify-start">
<div
class="flex rounded-md flex-shrink-0 gap-1 items-center px-2 py-1"
class="flex rounded-md max-w-full flex-shrink-0 gap-1 items-center px-2 py-1 xs:w-full overflow-hidden"
:class="{
'!bg-orange-50 !text-orange-500': relation === 'hm',
'!bg-pink-50 !text-pink-500': relation === 'mm',
@ -85,34 +89,36 @@ const relationMeta = computed(() => {
}"
>
<MdiFileDocumentMultipleOutline
class="w-4 h-4"
class="w-4 h-4 min-w-4"
:class="{
'!text-orange-500': relation === 'hm',
'!text-pink-500': relation === 'mm',
'!text-blue-500': relation === 'bt',
}"
/>
{{ relatedTableTitle }} Records
<span class="truncate"> {{ relatedTableTitle }} Records </span>
</div>
</div>
</div>
<NcTooltip class="z-10" placement="bottom">
<template #title>
<div class="p-1">
<h1 class="text-white font-bold">{{ relationMeta.title }}</h1>
<div class="text-white">
{{ relationMeta.tooltip_desc }}
<span class="bg-gray-700 px-2 rounded-md">
{{ tableTitle }}
</span>
{{ relationMeta.tooltip_desc2 }}
<span class="bg-gray-700 px-2 rounded-md">
{{ relatedTableTitle }}
</span>
<div v-if="!isMobileMode" class="flex flex-row justify-end w-36">
<NcTooltip class="z-10" placement="bottom">
<template #title>
<div class="p-1">
<h1 class="text-white font-bold">{{ relationMeta.title }}</h1>
<div class="text-white">
{{ relationMeta.tooltip_desc }}
<span class="bg-gray-700 px-2 rounded-md">
{{ tableTitle }}
</span>
{{ relationMeta.tooltip_desc2 }}
<span class="bg-gray-700 px-2 rounded-md">
{{ relatedTableTitle }}
</span>
</div>
</div>
</div>
</template>
<InfoIcon class="w-4 h-4 mt-2" />
</NcTooltip>
</template>
<InfoIcon class="w-4 h-4" />
</NcTooltip>
</div>
</div>
</template>

197
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -24,6 +24,8 @@ const emit = defineEmits(['update:modelValue', 'attachRecord'])
const vModel = useVModel(props, 'modelValue', emit)
const { isMobileMode } = useGlobal()
const isForm = inject(IsFormInj, ref(false))
const isPublic = inject(IsPublicInj, ref(false))
@ -88,7 +90,7 @@ const isFocused = ref(false)
const fields = computedInject(FieldsInj, (_fields) => {
return (relatedTableMeta.value.columns ?? [])
.filter((col) => !isSystemColumn(col) && !isPrimary(col) && !isLinksOrLTAR(col) && !isAttachment(col))
.slice(0, 4)
.slice(0, isMobileMode.value ? 1 : 4)
})
const expandedFormDlg = ref(false)
@ -159,13 +161,14 @@ const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
</script>
<template>
<a-modal
<NcModal
v-model:visible="vModel"
:class="{ active: vModel }"
:footer="null"
:closable="false"
size="medium"
:width="isForm ? 600 : 800"
:body-style="{ 'padding': 0, 'margin': 0, 'min-height': isForm ? '300px' : '500px' }"
:body-style="{ 'max-height': '640px', 'height': '85vh' }"
wrap-class-name="nc-modal-child-list"
>
<LazyVirtualCellComponentsHeader
@ -187,7 +190,7 @@ const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
ref="filterQueryRef"
v-model:value="childrenListPagination.query"
:placeholder="`Search in ${relatedTableMeta?.title}`"
class="w-full !rounded-md"
class="w-full !sm:rounded-md xs:min-h-8 !xs:rounded-xl"
size="small"
:bordered="false"
@focus="isFocused = true"
@ -199,107 +202,113 @@ const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
</div>
</div>
<div v-if="isDataExist || isChildrenLoading" class="mt-2 mb-2">
<div
:class="{
'h-[420px]': !isForm,
'h-[250px]': isForm,
}"
class="overflow-scroll nc-scrollbar-md cursor-pointer pr-1"
>
<template v-if="isChildrenLoading">
<div
v-for="(x, i) in Array.from({ length: skeltonAmountToShow })"
:key="i"
class="border-2 flex flex-row gap-2 mb-2 transition-all rounded-xl relative border-gray-200 hover:bg-gray-50"
>
<a-skeleton-image class="h-24 w-24 !rounded-xl" />
<div class="flex flex-col m-[.5rem] gap-2 flex-grow justify-center">
<a-skeleton-input class="!w-48 !rounded-xl" active size="small" />
<div class="flex flex-row gap-6 w-10/12">
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
<div class="flex flex-col flex-grow nc-scrollbar-md">
<template v-if="(isNew && state?.[colTitle]?.length) || childrenList?.pageInfo?.totalRows">
<div class="cursor-pointer pr-1">
<template v-if="isChildrenLoading">
<div
v-for="(x, i) in Array.from({ length: 10 })"
:key="i"
class="!border-2 flex flex-row gap-2 mb-2 transition-all !rounded-xl relative !border-gray-200 hover:bg-gray-50"
>
<a-skeleton-image class="h-24 w-24 !rounded-xl" />
<div class="flex flex-col m-[.5rem] gap-2 flex-grow justify-center">
<a-skeleton-input class="!w-48 !rounded-xl" active size="small" />
<div class="flex flex-row gap-6 w-10/12">
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<LazyVirtualCellComponentsListItem
v-for="(refRow, id) in childrenList?.list ?? state?.[colTitle] ?? []"
:key="id"
:row="refRow"
:fields="fields"
data-testid="nc-child-list-item"
:attachment="attachmentCol"
:related-table-display-value-prop="relatedTableDisplayValueProp"
:is-linked="childrenList?.list ? isChildrenListLinked[Number.parseInt(id)] : true"
:is-loading="isChildrenListLoading[Number.parseInt(id)]"
@expand="onClick(refRow)"
@click="linkOrUnLink(refRow, id)"
/>
</template>
</template>
<template v-else>
<LazyVirtualCellComponentsListItem
v-for="(refRow, id) in childrenList?.list ?? state?.[colTitle] ?? []"
:key="id"
:row="refRow"
:fields="fields"
data-testid="nc-child-list-item"
:attachment="attachmentCol"
:related-table-display-value-prop="relatedTableDisplayValueProp"
:is-linked="childrenList?.list ? isChildrenListLinked[Number.parseInt(id)] : true"
:is-loading="isChildrenListLoading[Number.parseInt(id)]"
@expand="onClick(refRow)"
@click="
() => {
if (isPublic && !isForm) return
isNew
? unlinkRow(refRow, Number.parseInt(id))
: isChildrenListLinked[Number.parseInt(id)]
? unlinkRow(refRow, Number.parseInt(id))
: linkRow(refRow, Number.parseInt(id))
}
"
/>
</template>
</div>
</template>
<div v-else class="pt-1 flex flex-col gap-3 my-auto items-center justify-center text-gray-500">
<InboxIcon class="w-16 h-16 mx-auto" />
<p>
{{ $t('msg.noRecordsAreLinkedFromTable') }}
{{ relatedTableMeta?.title }}
</p>
<NcButton
v-if="!readonly && childrenListCount < 1"
v-e="['c:links:link']"
data-testid="nc-child-list-button-link-to"
@click="emit('attachRecord')"
>
<div class="flex items-center gap-1"><MdiPlus /> {{ $t('title.linkMoreRecords') }}</div>
</NcButton>
</div>
</div>
<div
v-else
:class="{
'h-[420px]': !isForm,
'h-[250px]': isForm,
}"
class="pt-1 flex flex-col gap-3 items-center justify-center text-gray-500"
>
<InboxIcon class="w-16 h-16 mx-auto" />
<p>
{{ $t('msg.noRecordsAreLinkedFromTable') }}
{{ relatedTableMeta?.title }}
</p>
<NcButton
v-if="!readonly && childrenListCount < 1"
v-e="['c:links:link']"
data-testid="nc-child-list-button-link-to"
@click="emit('attachRecord')"
>
<div class="flex items-center gap-1"><MdiPlus /> {{ $t('title.linkMoreRecords') }}</div>
</NcButton>
<div v-if="isMobileMode" class="flex flex-row justify-center items-center w-full my-2">
<NcPagination
v-if="!isNew && childrenList?.pageInfo"
v-model:current="childrenListPagination.page"
v-model:page-size="childrenListPagination.size"
:total="+childrenList.pageInfo.totalRows!"
/>
</div>
<div class="my-2 bg-gray-50 border-gray-50 border-b-2"></div>
<div class="flex flex-row justify-between bg-white relative pt-1">
<div v-if="!isForm" class="flex items-center justify-center px-2 rounded-md text-gray-500 bg-brand-50">
{{ childrenListCount || 0 }} {{ $t('objects.records') }} {{ childrenListCount !== 0 ? $t('general.are') : '' }}
{{ childrenListCount || 0 }} {{ !isMobileMode ? $t('objects.records') : '' }}
{{ !isMobileMode && childrenListCount !== 0 ? $t('general.are') : '' }}
{{ $t('general.linked') }}
</div>
<div v-else class="flex items-center justify-center px-2 rounded-md text-gray-500 bg-brand-50">
{{ state?.[colTitle]?.length || 0 }} {{ $t('objects.records') }}
{{ state?.[colTitle]?.length !== 0 ? $t('general.are') : '' }}
{{ $t('general.linked') }}
<span class="">
{{ state?.[colTitle]?.length || 0 }} {{ $t('objects.records') }}
{{ state?.[colTitle]?.length !== 0 ? $t('general.are') : '' }}
{{ $t('general.linked') }}
</span>
</div>
<div class="flex absolute items-center py-2 justify-center w-full">
<a-pagination
<div class="!xs:hidden flex absolute -mt-0.75 items-center py-2 justify-center w-full">
<NcPagination
v-if="!isNew && childrenList?.pageInfo"
v-model:current="childrenListPagination.page"
v-model:page-size="childrenListPagination.size"
:total="+childrenList.pageInfo.totalRows!"
:show-size-changer="false"
class="mt-2 mx-auto"
size="small"
hide-on-single-page
show-less-items
mode="simple"
/>
</div>
<div class="flex flex-row gap-2">
@ -310,7 +319,9 @@ const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
data-testid="nc-child-list-button-link-to"
@click="emit('attachRecord')"
>
<div class="flex items-center gap-1"><MdiPlus /> {{ $t('title.linkMoreRecords') }}</div>
<div class="flex items-center gap-1">
<MdiPlus class="!xs:hidden" /> {{ isMobileMode ? $t('title.linkMore') : $t('title.linkMoreRecords') }}
</div>
</NcButton>
</div>
</div>
@ -333,11 +344,21 @@ const linkOrUnLink = (rowRef: Record<string, string>, id: string) => {
use-meta-fields
/>
</Suspense>
</a-modal>
</NcModal>
</template>
<style scoped lang="scss">
:deep(.nc-nested-list-item .ant-card-body) {
@apply !px-1 !py-0;
}
:deep(.ant-modal-content) {
@apply !p-0;
}
</style>
<style lang="scss">
.nc-modal-child-list > .ant-modal > .ant-modal-content {
@apply !p-0;
}
</style>

43
packages/nc-gui/components/virtual-cell/components/ListItem.vue

@ -47,6 +47,13 @@ interface Attachment {
mimetype: string
}
const isRowEmpty = (row: any, col: any) => {
const val = row[col.title]
if (!val) return true
return Array.isArray(val) && val.length === 0
}
const attachments: ComputedRef<Attachment[]> = computed(() => {
try {
if (props.attachment && row.value[props.attachment.title]) {
@ -87,9 +94,11 @@ const attachments: ComputedRef<Attachment[]> = computed(() => {
<div v-else-if="attachment" class="h-24 w-24 w-full !flex flex-row items-center !rounded-l-xl justify-center">
<img class="object-contain h-24 w-24" src="~assets/icons/FileIconImageBox.png" />
</div>
<div class="flex flex-col m-[.75rem] gap-1 flex-grow justify-center">
<div class="flex justify-between">
<span class="font-semibold text-gray-800 nc-display-value"> {{ row[relatedTableDisplayValueProp] }} </span>
<div class="flex flex-col m-[.75rem] gap-1 flex-grow justify-center overflow-hidden">
<div class="flex justify-between xs:gap-x-2">
<span class="font-semibold text-gray-800 nc-display-value xs:(truncate)">
{{ row[relatedTableDisplayValueProp] }}
</span>
<div
v-if="isLinked && !isLoading"
class="text-brand-500 text-0.875"
@ -109,8 +118,11 @@ const attachments: ComputedRef<Attachment[]> = computed(() => {
/>
</div>
<div v-if="fields.length > 0 && !isPublic && !isForm" class="flex ml-[-0.25rem] flex-row gap-4 w-10/12">
<div v-for="field in fields" :key="field.id" :class="attachment ? 'w-1/3' : 'w-1/4'">
<div
v-if="fields.length > 0 && !isPublic && !isForm"
class="flex ml-[-0.25rem] sm:flex-row xs:(flex-col mt-2) gap-4 w-10/12"
>
<div v-for="field in fields" :key="field.id" :class="attachment ? 'sm:w-1/3' : 'sm:w-1/4'">
<div class="flex flex-col gap-[-1] max-w-72">
<LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(field)"
@ -121,15 +133,18 @@ const attachments: ComputedRef<Attachment[]> = computed(() => {
/>
<LazySmartsheetHeaderCell v-else class="!scale-70" :column="field" :hide-menu="true" :hide-icon="true" />
<LazySmartsheetVirtualCell v-if="isVirtualCol(field)" v-model="row[field.title]" :row="row" :column="field" />
<LazySmartsheetCell
v-else
v-model="row[field.title]"
class="!text-gray-600 ml-1"
:column="field"
:edit-enabled="false"
:read-only="true"
/>
<div v-if="!isRowEmpty(row, field)">
<LazySmartsheetVirtualCell v-if="isVirtualCol(field)" v-model="row[field.title]" :row="row" :column="field" />
<LazySmartsheetCell
v-else
v-model="row[field.title]"
class="!text-gray-600 ml-1"
:column="field"
:edit-enabled="false"
:read-only="true"
/>
</div>
<div v-else class="flex flex-row w-full h-[1.375rem] pl-1 items-center justify-start">-</div>
</div>
</div>
</div>

170
packages/nc-gui/components/virtual-cell/components/ListItems.vue

@ -21,6 +21,8 @@ const emit = defineEmits(['update:modelValue', 'addNewRecord'])
const vModel = useVModel(props, 'modelValue', emit)
const { isMobileMode } = useGlobal()
const injectedColumn = inject(ColumnInj)
const filterQueryRef = ref()
@ -138,7 +140,7 @@ const attachmentCol = computedInject(FieldsInj, (_fields) => {
const fields = computedInject(FieldsInj, (_fields) => {
return (relatedTableMeta.value.columns ?? [])
.filter((col) => !isSystemColumn(col) && !isPrimary(col) && !isLinksOrLTAR(col) && !isAttachment(col))
.slice(0, 4)
.slice(0, isMobileMode.value ? 1 : 4)
})
const relation = computed(() => {
@ -157,13 +159,13 @@ onKeyStroke('Escape', () => {
</script>
<template>
<a-modal
<NcModal
v-model:visible="vModel"
:class="{ active: vModel }"
:footer="null"
:width="isForm ? 600 : 800"
:closable="false"
:body-style="{ 'padding': 0, 'margin': 0, 'min-height': '500px' }"
:body-style="{ 'max-height': '640px', 'height': '85vh' }"
wrap-class-name="nc-modal-link-record"
>
<LazyVirtualCellComponentsHeader
@ -173,7 +175,7 @@ onKeyStroke('Escape', () => {
:related-table-title="relatedTableMeta?.title"
:display-value="row.row[displayValueProp]"
/>
<div class="m-4 bg-gray-50 border-gray-50 border-b-2"></div>
<div class="!xs:hidden my-3 bg-gray-50 border-gray-50 border-b-2"></div>
<div class="flex mt-2 mb-2 items-center gap-2">
<div
class="flex items-center border-1 p-1 rounded-md w-full border-gray-200"
@ -184,7 +186,7 @@ onKeyStroke('Escape', () => {
ref="filterQueryRef"
v-model:value="childrenExcludedListPagination.query"
:placeholder="`${$t('general.searchIn')} ${relatedTableMeta?.title}`"
class="w-full !rounded-md nc-excluded-search"
class="w-full !rounded-md nc-excluded-search xs:min-h-8"
size="small"
:bordered="false"
@focus="isFocused = true"
@ -202,7 +204,7 @@ onKeyStroke('Escape', () => {
v-if="!isPublic"
v-e="['c:row-expand:open']"
type="secondary"
size="xl"
:size="isMobileMode ? 'medium' : 'small'"
class="!text-brand-500"
@click="
() => {
@ -211,99 +213,103 @@ onKeyStroke('Escape', () => {
}
"
>
<div class="flex items-center gap-1"><MdiPlus /> {{ $t('activity.newRecord') }}</div>
<div class="flex items-center gap-1 px-4"><MdiPlus v-if="!isMobileMode" /> {{ $t('activity.newRecord') }}</div>
</NcButton>
</div>
<template v-if="childrenExcludedList?.pageInfo?.totalRows || isChildrenExcludedLoading">
<div class="pb-2 pt-1">
<div class="h-[420px] overflow-scroll nc-scrollbar-md pr-1 cursor-pointer">
<template v-if="isChildrenExcludedLoading">
<div
v-for="(x, i) in Array.from({ length: 10 })"
:key="i"
class="!border-2 flex flex-row gap-2 mb-2 transition-all !rounded-xl relative !border-gray-200 hover:bg-gray-50"
>
<a-skeleton-image class="h-24 w-24 !rounded-xl" />
<div class="flex flex-col m-[.5rem] gap-2 flex-grow justify-center">
<a-skeleton-input class="!w-48 !rounded-xl" active size="small" />
<div class="flex flex-row gap-6 w-10/12">
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!h-4 !w-24" active size="small" />
</div>
<template v-if="childrenExcludedList?.pageInfo?.totalRows">
<div class="overflow-scroll nc-scrollbar-md pr-1 cursor-pointer flex flex-col flex-grow">
<template v-if="isChildrenExcludedLoading">
<div
v-for="(x, i) in Array.from({ length: 10 })"
:key="i"
class="!border-2 flex flex-row gap-2 mb-2 transition-all !rounded-xl relative !border-gray-200 hover:bg-gray-50"
>
<a-skeleton-image class="h-24 w-24 !rounded-xl" />
<div class="flex flex-col m-[.5rem] gap-2 flex-grow justify-center">
<a-skeleton-input class="!xs:w-30 !w-48 !rounded-xl" active size="small" />
<div class="flex flex-row gap-6 w-10/12">
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!xs:hidden !h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!xs:hidden !h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!xs:hidden !h-4 !w-24" active size="small" />
</div>
<div class="flex flex-col gap-0.5">
<a-skeleton-input class="!h-4 !w-12" active size="small" />
<a-skeleton-input class="!xs:hidden !h-4 !w-24" active size="small" />
</div>
</div>
</div>
</template>
<template v-else>
<LazyVirtualCellComponentsListItem
v-for="(refRow, id) in childrenExcludedList?.list ?? []"
:key="id"
data-testid="nc-excluded-list-item"
:row="refRow"
:fields="fields"
:attachment="attachmentCol"
:related-table-display-value-prop="relatedTableDisplayValueProp"
:is-loading="isChildrenExcludedListLoading[Number.parseInt(id)]"
:is-linked="isChildrenExcludedListLinked[Number.parseInt(id)]"
@expand="
() => {
expandedFormRow = refRow
expandedFormDlg = true
}
"
@click="
() => {
if (isChildrenExcludedListLinked[Number.parseInt(id)]) unlinkRow(refRow, Number.parseInt(id))
else linkRow(refRow, Number.parseInt(id))
}
"
/>
</template>
</div>
</div>
</template>
<template v-else>
<LazyVirtualCellComponentsListItem
v-for="(refRow, id) in childrenExcludedList?.list ?? []"
:key="id"
data-testid="nc-excluded-list-item"
:row="refRow"
:fields="fields"
:attachment="attachmentCol"
:related-table-display-value-prop="relatedTableDisplayValueProp"
:is-loading="isChildrenExcludedListLoading[Number.parseInt(id)]"
:is-linked="isChildrenExcludedListLinked[Number.parseInt(id)]"
@expand="
() => {
expandedFormRow = refRow
expandedFormDlg = true
}
"
@click="
() => {
if (isChildrenExcludedListLinked[Number.parseInt(id)]) unlinkRow(refRow, Number.parseInt(id))
else linkRow(refRow, Number.parseInt(id))
}
"
/>
</template>
</div>
</template>
<div
v-if="!isChildrenExcludedLoading && !childrenExcludedList?.pageInfo?.totalRows"
class="py-2 h-[420px] flex flex-col gap-3 items-center justify-center text-gray-500"
>
<div v-else class="my-auto py-2 flex flex-col gap-3 items-center justify-center text-gray-500">
<InboxIcon class="w-16 h-16 mx-auto" />
<p>
{{ $t('msg.thereAreNoRecordsInTable') }}
{{ relatedTableMeta?.title }}
</p>
</div>
<div class="my-2 bg-gray-50 border-gray-50 border-b-2"></div>
<div class="flex flex-row justify-between bg-white relative pt-1">
<div v-if="!isForm" class="flex items-center justify-center px-2 rounded-md text-gray-500 bg-brand-50">
<div v-if="isMobileMode" class="flex flex-row justify-center items-center w-full my-2">
<NcPagination
v-if="childrenExcludedList?.pageInfo"
v-model:current="childrenExcludedListPagination.page"
v-model:page-size="childrenExcludedListPagination.size"
:total="+childrenExcludedList.pageInfo.totalRows"
entity-name="links-excluded-list"
/>
</div>
<div class="mb-2 bg-gray-50 border-gray-50 border-b-2"></div>
<div class="flex flex-row justify-between items-center bg-white relative pt-1">
<div v-if="!isForm" class="flex items-center justify-center px-2 rounded-md text-gray-500 bg-brand-50 h-9.5">
{{ relation === 'bt' ? (row.row[relatedTableMeta?.title] ? '1' : 0) : childrenListCount ?? 'No' }}
{{ $t('objects.records') }} {{ childrenListCount !== 0 ? 'are' : '' }} {{ $t('general.linked') }}
{{ !isMobileMode ? $t('objects.records') : '' }} {{ !isMobileMode && childrenListCount !== 0 ? 'are' : '' }}
{{ $t('general.linked') }}
</div>
<div class="flex absolute items-center py-2 justify-center w-full">
<a-pagination
<div class="!xs:hidden flex absolute -mt-0.75 items-center py-2 justify-center w-full">
<NcPagination
v-if="childrenExcludedList?.pageInfo"
v-model:current="childrenExcludedListPagination.page"
v-model:page-size="childrenExcludedListPagination.size"
:total="+childrenExcludedList.pageInfo.totalRows"
:show-size-changer="false"
class="mt-2 mx-auto"
size="small"
hide-on-single-page
show-less-items
entity-name="links-excluded-list"
mode="simple"
/>
</div>
<NcButton class="nc-close-btn ml-auto" type="ghost" @click="vModel = false"> {{ $t('general.finish') }} </NcButton>
@ -327,5 +333,11 @@ onKeyStroke('Escape', () => {
use-meta-fields
/>
</Suspense>
</a-modal>
</NcModal>
</template>
<style lang="scss">
.nc-modal-link-record > .ant-modal > .ant-modal-content {
@apply !p-0;
}
</style>

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

@ -307,6 +307,7 @@
"hasMany": "Has Many",
"manyToMany": "Many to Many",
"virtualRelation": "Virtual Relation",
"linkMore": "Link More",
"linkMoreRecords": "Link more records",
"downloadFile": "Download File",
"renameTable": "Rename Table",

Loading…
Cancel
Save