Files
nocodb/packages/nc-gui/components/dashboard/settings/Metadata.vue
2026-01-13 14:46:10 +00:00

281 lines
7.9 KiB
Vue

<script setup lang="ts">
const props = defineProps<{
sourceId: string
}>()
const emit = defineEmits(['baseSynced'])
const { $api } = useNuxtApp()
const baseStore = useBase()
const { loadTables } = baseStore
const { base } = storeToRefs(baseStore)
const { t } = useI18n()
const progressRef = ref()
const isLoading = ref(false)
const isDifferent = ref(false)
const triggeredSync = ref(false)
const syncCompleted = ref(false)
const metadiff = ref<any[]>([])
const { $poller } = useNuxtApp()
async function loadMetaDiff(afterSync = false) {
try {
if (!base.value?.id) return
if (triggeredSync.value && !syncCompleted.value && !afterSync) return
isLoading.value = true
isDifferent.value = false
const jobData = await $api.source.metaDiffGet(base.value?.id, props.sourceId)
$poller.subscribe(
{ id: jobData.id },
async (data: {
id: string
status?: string
data?: {
error?: {
message: string
}
message?: string
result?: any
}
}) => {
if (data.status !== 'close') {
if (data.status === JobStatus.COMPLETED) {
metadiff.value = data.data?.result || []
for (const model of metadiff.value) {
if (model.detectedChanges?.length > 0) {
model.syncState = model.detectedChanges.map((el: any) => el?.msg).join(', ')
isDifferent.value = true
}
}
isLoading.value = false
if (afterSync) syncCompleted.value = true
} else if (data.status === JobStatus.FAILED) {
isLoading.value = false
if (afterSync) syncCompleted.value = true
console.error(data.data?.error?.message)
message.error('Failed to load metadata diff', undefined, {
copyText: data.data?.error?.message || '',
})
}
}
},
)
} catch (e) {
console.error(e)
}
}
const onBack = () => {
triggeredSync.value = false
syncCompleted.value = false
}
async function syncMetaDiff() {
try {
if (isLoading.value) return
if (!base.value?.id || !isDifferent.value) return
isLoading.value = true
triggeredSync.value = true
const jobData = await $api.source.metaDiffSync(base.value?.id, props.sourceId)
$poller.subscribe(
{ id: jobData.id },
async (data: {
id: string
status?: string
data?: {
error?: {
message: string
}
message?: string
result?: any
}
}) => {
if (data.status !== 'close') {
if (data.status === JobStatus.COMPLETED) {
// Table metadata recreated successfully
message.info(t('msg.info.metaDataRecreated'))
progressRef.value?.pushProgress('Done!', data.status)
isLoading.value = false
try {
await loadTables()
await loadMetaDiff(true)
} catch (_e: any) {
// ignore
}
emit('baseSynced')
} else if (data.status === JobStatus.FAILED) {
progressRef.value?.pushProgress(data.data?.error?.message || 'Failed to sync base metadata', data.status)
syncCompleted.value = true
isLoading.value = false
} else {
// Job is still in progress
progressRef.value?.pushProgress(data.data?.message)
}
}
},
)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
onMounted(async () => {
if (metadiff.value.length === 0) {
await loadMetaDiff()
}
})
const columns = [
{
// Models
title: t('labels.models'),
key: 'table_name',
name: 'table_name',
minWidth: 200,
padding: '0px 12px',
},
{
// Sync state
title: t('labels.syncState'),
dataIndex: 'syncState',
key: 'syncState',
minWidth: 200,
padding: '0px 12px',
},
]
const customRow = (record: Record<string, any>) => ({
class: `nc-metasync-row nc-metasync-row-${record.table_name}`,
})
</script>
<template>
<div class="h-full flex flex-col w-full">
<div class="h-full flex flex-col">
<div class="flex flex-row justify-between items-center w-full mb-4">
<div class="flex">
<div v-if="isDifferent">
<NcButton
v-e="['a:proj-meta:meta-data:sync']"
size="small"
class="nc-btn-metasync-sync-now"
:disabled="isLoading"
@click="syncMetaDiff"
>
<div class="flex items-center gap-2">
<component :is="iconMap.reload" v-if="isLoading" class="animate-infinite animate-spin" />
<component :is="iconMap.databaseSync" v-else />
{{ $t('activity.metaSync') }}
</div>
</NcButton>
</div>
<div v-else>
<!-- Tables metadata is in sync -->
<span>
<a-alert :message="$t('msg.info.tablesMetadataInSync')" type="success" show-icon class="!rounded-md" />
</span>
</div>
</div>
<!-- Reload -->
<NcButton
v-e="['a:proj-meta:meta-data:reload']"
size="small"
type="secondary"
class="self-start !rounded-md nc-btn-metasync-reload"
@click="loadMetaDiff()"
>
<div class="flex items-center gap-2 text-nc-content-gray-subtle2 font-light">
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
{{ $t('general.reload') }}
</div>
</NcButton>
</div>
<div v-if="triggeredSync" class="flex flex-col justify-center items-center h-full overflow-y-auto">
<GeneralProgressPanel ref="progressRef" class="w-1/2 h-full" />
<div class="flex justify-center">
<NcButton
html-type="submit"
class="mt-4 mb-8"
:class="{
'sync-completed': syncCompleted,
}"
size="medium"
:disabled="!syncCompleted"
@click="onBack"
>
{{ $t('general.back') }}
</NcButton>
</div>
</div>
<NcTable
v-else
:columns="columns"
:data="metadiff ?? []"
row-height="44px"
header-row-height="44px"
:is-data-loading="isLoading"
:custom-row="customRow"
class="nc-metasync-table h-[calc(100%_-_58px)] w-full"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'table_name'">
<div class="flex items-center gap-2 max-w-full">
<div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="record" class="text-nc-content-gray-muted" />
</div>
<NcTooltip class="truncate" show-on-truncate-only>
<template #title>{{ record.title || record.table_name }}</template>
{{ record.title || record.table_name }}
</NcTooltip>
</div>
</template>
<template v-if="column.key === 'syncState'">
<div class="flex items-center gap-2 max-w-full">
<NcTooltip class="truncate" show-on-truncate-only>
<template #title> {{ record?.syncState || $t('msg.info.metaNoChange') }} </template>
<span
:class="{
'text-nc-content-red-medium': record?.syncState,
'text-nc-content-gray-muted': !record?.syncState,
}"
>
{{ record?.syncState || $t('msg.info.metaNoChange') }}
</span>
</NcTooltip>
</div>
</template>
</template>
</NcTable>
</div>
<div class="flex place-content-center item-center">
<!-- Sync Now -->
</div>
</div>
</template>