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"
|
@open="openImage"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<ArtistDangerZone
|
||||||
|
:slug="slug"
|
||||||
|
:artist-id="store.overview.id"
|
||||||
|
:artist-name="store.overview.name"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
@@ -106,6 +112,7 @@ import { useRoute, useRouter, RouterLink } from 'vue-router'
|
|||||||
import { useArtistStore } from '../stores/artist.js'
|
import { useArtistStore } from '../stores/artist.js'
|
||||||
import { useModalStore } from '../stores/modal.js'
|
import { useModalStore } from '../stores/modal.js'
|
||||||
import MasonryGrid from '../components/discovery/MasonryGrid.vue'
|
import MasonryGrid from '../components/discovery/MasonryGrid.vue'
|
||||||
|
import ArtistDangerZone from '../components/artist/ArtistDangerZone.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
Reference in New Issue
Block a user