Browse Source

Merge pull request #6643 from nocodb/nc-fix/better-ux-duplicate

fix: better ux for duplicate modals
pull/6647/head
mertmit 1 year ago committed by GitHub
parent
commit
d6605fa466
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 50
      packages/nc-gui/components/dashboard/TreeView/index.vue
  2. 76
      packages/nc-gui/components/dlg/ProjectDuplicate.vue
  3. 2
      packages/nc-gui/components/dlg/SharedBaseDuplicate.vue
  4. 74
      packages/nc-gui/components/dlg/TableDuplicate.vue

50
packages/nc-gui/components/dashboard/TreeView/index.vue

@ -1,11 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TableType } from 'nocodb-sdk' import type { TableType } from 'nocodb-sdk'
import { message } from 'ant-design-vue'
import ProjectWrapper from './ProjectWrapper.vue' import ProjectWrapper from './ProjectWrapper.vue'
import type { TabType } from '#imports'
import { import {
TreeViewInj, TreeViewInj,
computed, computed,
@ -22,16 +19,13 @@ import {
useNuxtApp, useNuxtApp,
useRoles, useRoles,
useTablesStore, useTablesStore,
useTabs,
} from '#imports' } from '#imports'
import { useRouter } from '#app' import { useRouter } from '#app'
const { isUIAllowed } = useRoles() const { isUIAllowed } = useRoles()
const { addTab } = useTabs() const { $e } = useNuxtApp()
const { $e, $poller } = useNuxtApp()
const router = useRouter() const router = useRouter()
@ -45,22 +39,14 @@ const { bases, basesList, activeProjectId } = storeToRefs(basesStore)
const { isWorkspaceLoading } = storeToRefs(useWorkspace()) const { isWorkspaceLoading } = storeToRefs(useWorkspace())
const { openTable } = useTablesStore()
const baseCreateDlg = ref(false) const baseCreateDlg = ref(false)
const baseStore = useBase() const baseStore = useBase()
const { loadTables } = baseStore const { isSharedBase } = storeToRefs(baseStore)
const { tables, isSharedBase } = storeToRefs(baseStore)
const { t } = useI18n()
const { activeTable: _activeTable } = storeToRefs(useTablesStore()) const { activeTable: _activeTable } = storeToRefs(useTablesStore())
const { refreshCommandPalette } = useCommandPalette()
const contextMenuTarget = reactive<{ type?: 'base' | 'source' | 'table' | 'main' | 'layout'; value?: any }>({}) const contextMenuTarget = reactive<{ type?: 'base' | 'source' | 'table' | 'main' | 'layout'; value?: any }>({})
const setMenuContext = (type: 'base' | 'source' | 'table' | 'main' | 'layout', value?: any) => { const setMenuContext = (type: 'base' | 'source' | 'table' | 'main' | 'layout', value?: any) => {
@ -120,38 +106,6 @@ const duplicateTable = async (table: TableType) => {
const { close } = useDialog(resolveComponent('DlgTableDuplicate'), { const { close } = useDialog(resolveComponent('DlgTableDuplicate'), {
'modelValue': isOpen, 'modelValue': isOpen,
'table': table, 'table': table,
'onOk': async (jobData: { id: string }) => {
$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) {
await loadTables()
refreshCommandPalette()
const newTable = tables.value.find((el) => el.id === data?.data?.result?.id)
if (newTable) addTab({ title: newTable.title, id: newTable.id, type: newTable.type as TabType })
openTable(newTable!)
} else if (data.status === JobStatus.FAILED) {
message.error(t('msg.error.failedToDuplicateTable'))
await loadTables()
}
}
},
)
$e('a:table:duplicate')
},
'onUpdate:modelValue': closeDialog, 'onUpdate:modelValue': closeDialog,
}) })

76
packages/nc-gui/components/dlg/ProjectDuplicate.vue

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import tinycolor from 'tinycolor2' import tinycolor from 'tinycolor2'
import type { BaseType } from 'nocodb-sdk' import type { BaseType } from 'nocodb-sdk'
import { useVModel } from '#imports' import { isEeUI, useVModel } from '#imports'
const props = defineProps<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
@ -15,6 +15,15 @@ const { refreshCommandPalette } = useCommandPalette()
const { api } = useApi() const { api } = useApi()
const { $e, $poller } = useNuxtApp()
const basesStore = useBases()
const { loadProjects, createProject: _createProject } = basesStore
const { bases } = storeToRefs(basesStore)
const { navigateToProject } = useGlobal()
const dialogShow = useVModel(props, 'modelValue', emit) const dialogShow = useVModel(props, 'modelValue', emit)
const options = ref({ const options = ref({
@ -57,12 +66,51 @@ const _duplicate = async () => {
}), }),
}, },
}) })
props.onOk(jobData as any)
$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) {
await loadProjects('workspace')
const base = bases.value.get(jobData.base_id)
// open project after duplication
if (base) {
await navigateToProject({
workspaceId: isEeUI ? base.fk_workspace_id : undefined,
baseId: base.id,
type: base.type,
})
}
refreshCommandPalette()
isLoading.value = false
dialogShow.value = false
} else if (data.status === JobStatus.FAILED) {
message.error('Failed to duplicate project')
await loadProjects('workspace')
refreshCommandPalette()
isLoading.value = false
dialogShow.value = false
}
}
},
)
$e('a:base:duplicate')
} catch (e: any) { } catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} finally {
isLoading.value = false isLoading.value = false
refreshCommandPalette()
dialogShow.value = false dialogShow.value = false
} }
} }
@ -78,7 +126,15 @@ const isEaster = ref(false)
</script> </script>
<template> <template>
<GeneralModal v-if="base" v-model:visible="dialogShow" class="!w-[30rem]" wrap-class-name="nc-modal-base-duplicate"> <GeneralModal
v-if="base"
v-model:visible="dialogShow"
:closable="!isLoading"
:mask-closable="!isLoading"
:keyboard="!isLoading"
class="!w-[30rem]"
wrap-class-name="nc-modal-base-duplicate"
>
<div> <div>
<div class="prose-xl font-bold self-center" @dblclick="isEaster = !isEaster"> <div class="prose-xl font-bold self-center" @dblclick="isEaster = !isEaster">
{{ $t('general.duplicate') }} {{ $t('objects.project') }} {{ $t('general.duplicate') }} {{ $t('objects.project') }}
@ -91,13 +147,15 @@ const isEaster = ref(false)
<a-divider class="!m-0 !p-0 !my-2" /> <a-divider class="!m-0 !p-0 !my-2" />
<div class="text-xs p-2"> <div class="text-xs p-2">
<a-checkbox v-model:checked="options.includeData">{{ $t('labels.includeData') }}</a-checkbox> <a-checkbox v-model:checked="options.includeData" :disabled="isLoading">{{ $t('labels.includeData') }}</a-checkbox>
<a-checkbox v-model:checked="options.includeViews">{{ $t('labels.includeView') }}</a-checkbox> <a-checkbox v-model:checked="options.includeViews" :disabled="isLoading">{{ $t('labels.includeView') }}</a-checkbox>
<a-checkbox v-show="isEaster" v-model:checked="options.includeHooks">{{ $t('labels.includeWebhook') }}</a-checkbox> <a-checkbox v-show="isEaster" v-model:checked="options.includeHooks" :disabled="isLoading">
{{ $t('labels.includeWebhook') }}
</a-checkbox>
</div> </div>
</div> </div>
<div class="flex flex-row gap-x-2 mt-2.5 pt-2.5 justify-end"> <div class="flex flex-row gap-x-2 mt-2.5 pt-2.5 justify-end">
<NcButton key="back" type="secondary" @click="dialogShow = false">{{ $t('general.cancel') }}</NcButton> <NcButton v-if="!isLoading" key="back" type="secondary" @click="dialogShow = false">{{ $t('general.cancel') }}</NcButton>
<NcButton key="submit" v-e="['a:base:duplicate']" :loading="isLoading" @click="_duplicate" <NcButton key="submit" v-e="['a:base:duplicate']" :loading="isLoading" @click="_duplicate"
>{{ $t('general.confirm') }} >{{ $t('general.confirm') }}
</NcButton> </NcButton>

2
packages/nc-gui/components/dlg/SharedBaseDuplicate.vue

@ -94,6 +94,8 @@ const _duplicate = async () => {
$e('a:base:duplicate-shared-base') $e('a:base:duplicate-shared-base')
} catch (e: any) { } catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
isLoading.value = false
dialogShow.value = false
} }
} }
</script> </script>

74
packages/nc-gui/components/dlg/TableDuplicate.vue

@ -1,6 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TableType } from 'nocodb-sdk' import type { TableType } from 'nocodb-sdk'
import { message } from 'ant-design-vue'
import { useVModel } from '#imports' import { useVModel } from '#imports'
import type { TabType } from '#imports'
const props = defineProps<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
@ -14,6 +16,26 @@ const { api } = useApi()
const dialogShow = useVModel(props, 'modelValue', emit) const dialogShow = useVModel(props, 'modelValue', emit)
const { addTab } = useTabs()
const { $e, $poller } = useNuxtApp()
const basesStore = useBases()
const { createProject: _createProject } = basesStore
const { openTable } = useTablesStore()
const baseStore = useBase()
const { loadTables } = baseStore
const { tables } = storeToRefs(baseStore)
const { t } = useI18n()
const { activeTable: _activeTable } = storeToRefs(useTablesStore())
const { refreshCommandPalette } = useCommandPalette() const { refreshCommandPalette } = useCommandPalette()
const options = ref({ const options = ref({
@ -37,13 +59,45 @@ const _duplicate = async () => {
try { try {
isLoading.value = true isLoading.value = true
const jobData = await api.dbTable.duplicate(props.table.base_id!, props.table.id!, { options: optionsToExclude.value }) const jobData = await api.dbTable.duplicate(props.table.base_id!, props.table.id!, { options: optionsToExclude.value })
props.onOk(jobData as any)
$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) {
await loadTables()
refreshCommandPalette()
const newTable = tables.value.find((el) => el.id === data?.data?.result?.id)
if (newTable) addTab({ title: newTable.title, id: newTable.id, type: newTable.type as TabType })
openTable(newTable!)
isLoading.value = false
dialogShow.value = false
} else if (data.status === JobStatus.FAILED) {
message.error(t('msg.error.failedToDuplicateTable'))
await loadTables()
isLoading.value = false
dialogShow.value = false
}
}
},
)
$e('a:table:duplicate')
} catch (e: any) { } catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} finally {
isLoading.value = false isLoading.value = false
dialogShow.value = false dialogShow.value = false
refreshCommandPalette()
} }
} }
@ -61,10 +115,12 @@ const isEaster = ref(false)
<GeneralModal <GeneralModal
v-model:visible="dialogShow" v-model:visible="dialogShow"
:class="{ active: dialogShow }" :class="{ active: dialogShow }"
:closable="!isLoading"
:mask-closable="!isLoading"
:keyboard="!isLoading"
centered centered
wrap-class-name="nc-modal-table-duplicate" wrap-class-name="nc-modal-table-duplicate"
:footer="null" :footer="null"
:closable="false"
class="!w-[30rem]" class="!w-[30rem]"
@keydown.esc="dialogShow = false" @keydown.esc="dialogShow = false"
> >
@ -80,13 +136,15 @@ const isEaster = ref(false)
<a-divider class="!m-0 !p-0 !my-2" /> <a-divider class="!m-0 !p-0 !my-2" />
<div class="text-xs p-2"> <div class="text-xs p-2">
<a-checkbox v-model:checked="options.includeData">{{ $t('labels.includeData') }}</a-checkbox> <a-checkbox v-model:checked="options.includeData" :disabled="isLoading">{{ $t('labels.includeData') }}</a-checkbox>
<a-checkbox v-model:checked="options.includeViews">{{ $t('labels.includeView') }}</a-checkbox> <a-checkbox v-model:checked="options.includeViews" :disabled="isLoading">{{ $t('labels.includeView') }}</a-checkbox>
<a-checkbox v-show="isEaster" v-model:checked="options.includeHooks">{{ $t('labels.includeWebhook') }}</a-checkbox> <a-checkbox v-show="isEaster" v-model:checked="options.includeHooks" :disabled="isLoading">
{{ $t('labels.includeWebhook') }}
</a-checkbox>
</div> </div>
</div> </div>
<div class="flex flex-row gap-x-2 mt-2.5 pt-2.5 justify-end"> <div class="flex flex-row gap-x-2 mt-2.5 pt-2.5 justify-end">
<NcButton key="back" type="secondary" @click="dialogShow = false">{{ $t('general.cancel') }}</NcButton> <NcButton v-if="!isLoading" key="back" type="secondary" @click="dialogShow = false">{{ $t('general.cancel') }}</NcButton>
<NcButton key="submit" v-e="['a:table:duplicate']" type="primary" :loading="isLoading" @click="_duplicate" <NcButton key="submit" v-e="['a:table:duplicate']" type="primary" :loading="isLoading" @click="_duplicate"
>{{ $t('general.confirm') }} >{{ $t('general.confirm') }}
</NcButton> </NcButton>

Loading…
Cancel
Save