fc3k(ui): ArtistDangerZone card + slot at bottom of ArtistView with cascade-delete flow
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<v-card class="fc-danger-zone mt-8" variant="outlined">
|
||||
<v-card-title class="d-flex align-center" style="gap: 10px;">
|
||||
<v-icon icon="mdi-alert-octagon" color="error" size="small" />
|
||||
<span>Danger zone</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="text-body-2 fc-muted mb-4">
|
||||
Cascade-delete this artist and every image, source, post, and
|
||||
attachment associated with them. This cannot be undone.
|
||||
Recoverable only from an FC-3h backup.
|
||||
</p>
|
||||
<v-btn
|
||||
color="error" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-delete-forever"
|
||||
:loading="loading"
|
||||
@click="onClick"
|
||||
>Delete artist & cascade</v-btn>
|
||||
|
||||
<DestructiveConfirmModal
|
||||
v-model="modalOpen"
|
||||
action="delete"
|
||||
kind="artist"
|
||||
:run-id="artistId"
|
||||
tier="C"
|
||||
:projected-counts="projectedCounts"
|
||||
:description="modalDescription"
|
||||
@confirm="onConfirm"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import DestructiveConfirmModal from '../modal/DestructiveConfirmModal.vue'
|
||||
import { useAdminStore } from '../../stores/admin.js'
|
||||
|
||||
const props = defineProps({
|
||||
slug: { type: String, required: true },
|
||||
artistId: { type: Number, required: true },
|
||||
artistName: { type: String, required: true },
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const store = useAdminStore()
|
||||
const loading = ref(false)
|
||||
const modalOpen = ref(false)
|
||||
const projected = ref(null)
|
||||
|
||||
const projectedCounts = computed(() => projected.value?.projected || null)
|
||||
|
||||
const modalDescription = computed(
|
||||
() => projected.value
|
||||
? `Artist “${props.artistName}” — `
|
||||
+ `${projected.value.projected.images} images, `
|
||||
+ `${projected.value.projected.sources} sources, `
|
||||
+ `${Math.round(projected.value.projected.bytes_on_disk / 1_048_576)} MiB on disk`
|
||||
: '',
|
||||
)
|
||||
|
||||
async function onClick() {
|
||||
loading.value = true
|
||||
try {
|
||||
projected.value = await store.projectArtistCascade(props.slug)
|
||||
modalOpen.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onConfirm(token) {
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await store.dispatchArtistCascade(props.slug, token)
|
||||
const taskId = result.task_id
|
||||
router.push('/artists')
|
||||
if (taskId) {
|
||||
store.pollTaskUntilDone(taskId).catch(() => {})
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fc-danger-zone {
|
||||
border-color: rgb(var(--v-theme-error));
|
||||
border-radius: 8px;
|
||||
}
|
||||
.fc-muted { color: rgb(var(--v-theme-on-surface-variant)); }
|
||||
</style>
|
||||
@@ -96,6 +96,12 @@
|
||||
@open="openImage"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<ArtistDangerZone
|
||||
:slug="slug"
|
||||
:artist-id="store.overview.id"
|
||||
:artist-name="store.overview.name"
|
||||
/>
|
||||
</template>
|
||||
</v-container>
|
||||
</template>
|
||||
@@ -106,6 +112,7 @@ import { useRoute, useRouter, RouterLink } from 'vue-router'
|
||||
import { useArtistStore } from '../stores/artist.js'
|
||||
import { useModalStore } from '../stores/modal.js'
|
||||
import MasonryGrid from '../components/discovery/MasonryGrid.vue'
|
||||
import ArtistDangerZone from '../components/artist/ArtistDangerZone.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
Reference in New Issue
Block a user