Browse Source

fix: snapshot creare and delete modal and ui

pull/9879/head
DarkPhoenix2704 3 days ago
parent
commit
11252f07f9
  1. 46
      packages/nc-gui/components/dashboard/settings/BaseSettings/Snapshots.vue
  2. 51
      packages/nc-gui/components/dlg/Snapshot/Delete.vue
  3. 70
      packages/nc-gui/components/dlg/Snapshot/Restore.vue
  4. 45
      packages/nc-gui/composables/useBaseSettings.ts
  5. 1
      packages/nc-gui/lang/en.json

46
packages/nc-gui/components/dashboard/settings/BaseSettings/Snapshots.vue

@ -3,8 +3,6 @@ import dayjs from 'dayjs'
const { t } = useI18n()
const { $api } = useNuxtApp()
const { sorts, sortDirection, loadSorts, handleGetSortedData, saveOrUpdate: saveOrUpdateSort } = useUserSorts('Webhook')
const orderBy = computed<Record<string, SordDirectionType>>({
@ -32,13 +30,10 @@ const {
createSnapshot,
listSnapshots,
updateSnapshot,
restoreSnapshot,
deleteSnapshot,
cancelNewSnapshot,
isUnsavedSnapshotsPending,
addNewSnapshot,
isCreatingSnapshot,
isRestoringSnapshot,
} = useBaseSettings()
const columns = [
@ -63,13 +58,46 @@ const columns = [
onMounted(async () => {
await listSnapshots()
})
const deleteSnapshot = (s: SnapshotExtendedType) => {
const isOpen = ref(true)
const { close } = useDialog(resolveComponent('DlgSnapshotDelete'), {
'modelValue': isOpen,
'snapshot': s,
'onUpdate:modelValue': closeDialog,
'onDeleted': async () => {
closeDialog()
await listSnapshots()
},
})
function closeDialog() {
isOpen.value = false
close(1000)
}
}
const restoreSnapshot = (s: SnapshotExtendedType) => {
const isOpen = ref(true)
const { close } = useDialog(resolveComponent('DlgSnapshotRestore'), {
'modelValue': isOpen,
'snapshot': s,
'onUpdate:modelValue': closeDialog,
'onRestored': async () => {
closeDialog()
},
})
function closeDialog() {
isOpen.value = false
close(1000)
}
}
</script>
<template>
<div
v-if="isCreatingSnapshot || isRestoringSnapshot"
class="absolute w-full h-full inset-0 flex items-center justify-center z-90 bg-black/12"
>
<div v-if="isCreatingSnapshot" class="absolute w-full h-full inset-0 flex items-center justify-center z-90 bg-black/12">
<div
v-if="isCreatingSnapshot"
style="box-shadow: 0px 8px 8px -4px rgba(0, 0, 0, 0.04), 0px 20px 24px -4px rgba(0, 0, 0, 0.1)"

51
packages/nc-gui/components/dlg/Snapshot/Delete.vue

@ -0,0 +1,51 @@
<script lang="ts" setup>
interface Props {
modelValue: boolean
snapshot: SnapshotExtendedType
}
interface Emits {
(event: 'update:modelValue', data: boolean): void
(event: 'deleted'): void
}
const props = defineProps<Props>()
const emits = defineEmits<Emits>()
const { snapshot } = props
const vModel = useVModel(props, 'modelValue', emits)
const { deleteSnapshot } = useBaseSettings()
async function onDelete() {
if (!snapshot.id) return
try {
await deleteSnapshot(snapshot)
vModel.value = false
emits('deleted')
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
}
</script>
<template>
<GeneralDeleteModal v-model:visible="vModel" :entity-name="$t('general.snapshot')" :on-delete="onDelete">
<template #entity-preview>
<div class="flex flex-row items-center py-2 px-3 bg-gray-50 rounded-lg text-gray-700">
<div
class="capitalize text-ellipsis overflow-hidden select-none w-full pl-3"
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
>
<span>
{{ snapshot.title }}
</span>
</div>
</div>
</template>
</GeneralDeleteModal>
</template>

70
packages/nc-gui/components/dlg/Snapshot/Restore.vue

@ -0,0 +1,70 @@
<script lang="ts" setup>
interface Props {
modelValue: boolean
snapshot: SnapshotExtendedType
}
interface Emits {
(event: 'update:modelValue', data: boolean): void
(event: 'restored'): void
}
const props = defineProps<Props>()
const emits = defineEmits<Emits>()
const { snapshot } = props
const vModel = useVModel(props, 'modelValue', emits)
const { restoreSnapshot, isRestoringSnapshot } = useBaseSettings()
</script>
<template>
<NcModal
v-model:visible="vModel"
:mask-closable="!isRestoringSnapshot"
size="xs"
height="auto"
:show-separator="false"
nc-modal-class-name="!p-6"
>
<div class="text-nc-content-gray-emphasis font-semibold text-lg">Confirm Snapshot Restore</div>
<div class="text-nc-content-gray-subtle2 my-2 leading-5">Are you sure you want to restore this base snapshot.</div>
<div class="leading-5 text-nc-content-gray-subtle">Note:</div>
<ul class="list-disc leading-5 text-nc-content-gray-subtle pl-4 !mb-0">
<li>Restoring this snapshot will not affect the existing base.</li>
<li>
On restore, a new base
<span class="font-semibold">
{{ snapshot.title }}
</span>
will be created in the same workspace.
</li>
</ul>
<div class="my-5 px-4 py-2 bg-nc-bg-gray-light rounded-lg">
{{ snapshot.title }}
</div>
<div class="flex items-center gap-2 justify-end">
<NcButton :disabled="isRestoringSnapshot" type="secondary" size="small" @click="vModel = false">
{{ $t('general.cancel') }}
</NcButton>
<NcButton
:disabled="isRestoringSnapshot"
type="primary"
size="small"
:loading="isRestoringSnapshot"
@click="restoreSnapshot(snapshot)"
>
{{ isRestoringSnapshot ? 'Restoring Snapshot' : $t('labels.confirmRestore') }}
</NcButton>
</div>
</NcModal>
</template>

45
packages/nc-gui/composables/useBaseSettings.ts

@ -18,14 +18,21 @@ export const useBaseSettings = createSharedComposable(() => {
const basesStore = useBases()
const { loadProjects } = basesStore
const { navigateToProject } = useGlobal()
const { refreshCommandPalette } = useCommandPalette()
const _projectId = inject(ProjectIdInj, undefined)
const isCreatingSnapshot = ref(false)
const isRestoringSnapshot = ref(false)
const baseId = computed(() => _projectId?.value ?? base.value?.id)
const { basesUser } = storeToRefs(basesStore)
const { basesUser, bases } = storeToRefs(basesStore)
const baseUsers = computed(() => (baseId.value ? basesUser.value.get(baseId.value) || [] : []))
@ -46,14 +53,12 @@ export const useBaseSettings = createSharedComposable(() => {
}
const deleteSnapshot = async (snapshot: SnapshotExtendedType) => {
if (!baseId.value) return
try {
snapshot.loading = true
await $api.snapshot.delete(baseId.value, snapshot.id!)
snapshots.value = snapshots.value.filter((s) => s.id !== snapshot.id)
} catch (error) {
message.error(await extractSdkResponseErrorMsg(error))
snapshot.loading = false
snapshot.error = true
console.error(error)
}
}
@ -124,8 +129,8 @@ export const useBaseSettings = createSharedComposable(() => {
const restoreSnapshot = async (snapshot: SnapshotExtendedType) => {
if (!baseId.value) return
try {
await $api.snapshot.restore(baseId.value, snapshot.id!)
isRestoringSnapshot.value = true
await $api.snapshot.restore(baseId.value, snapshot.id!)
$poller.subscribe(
{ id: snapshot.id! },
@ -140,15 +145,29 @@ export const useBaseSettings = createSharedComposable(() => {
result?: any
}
}) => {
if (data.status !== 'close') {
if (data.status === JobStatus.COMPLETED) {
// Table metadata recreated successfully
message.info('Snapshot restored successfully')
isRestoringSnapshot.value = false
} else if (status === JobStatus.FAILED) {
message.error('Failed to restore snapshot')
isRestoringSnapshot.value = false
if (data.status === JobStatus.COMPLETED) {
await loadProjects('workspace')
const base = bases.value.get(data.id)
isRestoringSnapshot.value = false
// open project after snapshot success
if (base) {
await navigateToProject({
workspaceId: isEeUI ? base.fk_workspace_id : undefined,
baseId: base.id,
type: base.type,
})
}
message.info('Snapshot restored successfully')
refreshCommandPalette()
} else if (data.status === JobStatus.FAILED) {
message.error('Failed to restore snapshot')
await loadProjects('workspace')
isRestoringSnapshot.value = false
refreshCommandPalette()
}
},
)

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

@ -651,6 +651,7 @@
"lockedByUser": "Locked by {user}"
},
"labels": {
"confirmRestore": "Confirm Restore",
"visibilityAndDataHandling": "Visibility & Data Handling",
"visibilityConfigLabel": "Base specific additional configurations to customise data display & default behaviours.",
"miscBaseSettingsLabel": "Snapshots are complete backups of your database at the time of creation. Restoring a snapshot duplicates the data.",

Loading…
Cancel
Save