feat(settings): tidy Cleanup tab into sectioned compact tiles (pass 1)
The Cleanup + Maintenance sections had ~17 full-width stacked cards with long descriptions — a hunt to scan. Operator wants compact, sectioned, scannable tiles (2026-06-18: keep both tabs, group inside, compact tiles in a grid). New common/MaintenanceTile.vue: a compact expandable tile (icon + short title + one-line blurb collapsed; click the header to expand the full controls/preview/ result inline; keyboard-accessible button + focus ring; tints the icon, keeps a running task expanded). Cleanup tab (this pass) restructured into 3 sections — Import-filter audits (Min dimensions, Transparency, Single-color) / Duplicates & posts (Bare posts, Duplicate posts, Deduplicate videos, Gated-post previews) / Tags (Unused, Legacy, Reset content tagging, Standardize casing) — each a responsive grid of tiles. PostMaintenanceCard split into 2 tiles, TagMaintenanceCard into 4. Moved VideoDedupCard + GatedPurgeCard from the Maintenance tab here (both are destructive content cleanup). All card logic unchanged — only the chrome. Maintenance tab tiling is pass 2 (TODO noted in MaintenancePanel). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,100 +1,98 @@
|
||||
<template>
|
||||
<v-card class="fc-post-maint">
|
||||
<CardHeading icon="mdi-post-outline" title="Post maintenance" />
|
||||
<v-card-text>
|
||||
<p class="fc-muted text-body-2 mb-4">
|
||||
Remove <strong>bare posts</strong> — post rows with no images (neither
|
||||
their own nor a cross-posted duplicate) and no attachments. These are the
|
||||
empty “Post 12345 / (no description)” shells a backfill can leave when a
|
||||
post's only content was a duplicate that links to an earlier post. Posts
|
||||
that gained a duplicate-image link are spared.
|
||||
<MaintenanceTile
|
||||
icon="mdi-post-outline"
|
||||
title="Bare posts"
|
||||
blurb="Delete empty post shells with no images or attachments."
|
||||
destructive
|
||||
>
|
||||
<p class="fc-muted text-body-2 mb-3">
|
||||
Post rows with no images (neither their own nor a cross-posted duplicate)
|
||||
and no attachments — the empty “Post 12345 / (no description)” shells a
|
||||
backfill can leave when a post's only content was a duplicate that links to
|
||||
an earlier post. Posts that gained a duplicate-image link are spared.
|
||||
</p>
|
||||
|
||||
<v-alert
|
||||
v-if="store.lastError"
|
||||
type="warning" variant="tonal" density="compact" class="mb-3"
|
||||
>{{ store.lastError }}</v-alert>
|
||||
|
||||
<v-btn
|
||||
color="accent" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-magnify"
|
||||
:loading="loadingPreview"
|
||||
class="mb-3"
|
||||
@click="onPreview"
|
||||
>Preview bare posts</v-btn>
|
||||
|
||||
<div v-if="preview">
|
||||
<p class="text-body-2 mb-2">
|
||||
<strong>{{ preview.count }}</strong> bare post(s).
|
||||
<span v-if="preview.count > 50" class="fc-muted">Showing first 50.</span>
|
||||
<span v-else-if="!preview.count" class="fc-muted">Nothing to clean up.</span>
|
||||
</p>
|
||||
|
||||
<v-alert
|
||||
v-if="store.lastError"
|
||||
type="warning" variant="tonal" density="compact" class="mb-3"
|
||||
>{{ store.lastError }}</v-alert>
|
||||
|
||||
<SampleNameGrid
|
||||
v-if="preview.sample_names?.length"
|
||||
:names="preview.sample_names" class="mb-3"
|
||||
/>
|
||||
<v-btn
|
||||
color="accent" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-magnify"
|
||||
:loading="loadingPreview"
|
||||
class="mb-3"
|
||||
@click="onPreview"
|
||||
>Preview bare posts</v-btn>
|
||||
color="error" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-delete-sweep"
|
||||
:disabled="!preview.count"
|
||||
:loading="committing"
|
||||
@click="onCommit"
|
||||
>Delete {{ preview.count }} bare post(s)</v-btn>
|
||||
<span v-if="deleted != null" class="ml-3 text-caption text-success">
|
||||
Deleted {{ deleted }} ✓
|
||||
</span>
|
||||
</div>
|
||||
</MaintenanceTile>
|
||||
|
||||
<div v-if="preview">
|
||||
<p class="text-body-2 mb-2">
|
||||
<strong>{{ preview.count }}</strong> bare post(s).
|
||||
<span v-if="preview.count > 50" class="fc-muted">
|
||||
Showing first 50.
|
||||
</span>
|
||||
<span v-else-if="!preview.count" class="fc-muted">
|
||||
Nothing to clean up.
|
||||
</span>
|
||||
</p>
|
||||
<SampleNameGrid
|
||||
v-if="preview.sample_names?.length"
|
||||
:names="preview.sample_names" class="mb-3"
|
||||
/>
|
||||
<v-btn
|
||||
color="error" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-delete-sweep"
|
||||
:disabled="!preview.count"
|
||||
:loading="committing"
|
||||
@click="onCommit"
|
||||
>Delete {{ preview.count }} bare post(s)</v-btn>
|
||||
<span
|
||||
v-if="deleted != null"
|
||||
class="ml-3 text-caption text-success"
|
||||
>Deleted {{ deleted }} ✓</span>
|
||||
</div>
|
||||
<MaintenanceTile
|
||||
icon="mdi-merge"
|
||||
title="Duplicate posts"
|
||||
blurb="Merge gallery-dl + native duplicate post records into one."
|
||||
destructive
|
||||
>
|
||||
<p class="fc-muted text-body-2 mb-3">
|
||||
When an artist was first downloaded by gallery-dl and later re-walked by the
|
||||
native ingester, the same post can exist twice (once keyed by the file's
|
||||
attachment id, once by the real post id). This merges each pair onto one
|
||||
canonical post (keeping the native post-id key), moving images, attachments
|
||||
and links onto the survivor. Images themselves are never touched.
|
||||
</p>
|
||||
|
||||
<v-divider class="my-5" />
|
||||
<v-btn
|
||||
color="accent" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-magnify"
|
||||
:loading="loadingDupPreview"
|
||||
class="mb-3"
|
||||
@click="onPreviewDupes"
|
||||
>Preview duplicate posts</v-btn>
|
||||
|
||||
<p class="fc-muted text-body-2 mb-4">
|
||||
Unify <strong>duplicate posts</strong> — when an artist was first
|
||||
downloaded by gallery-dl and later re-walked by the native ingester, the
|
||||
same post can exist twice (once keyed by the file's attachment id, once by
|
||||
the real post id). This merges each pair onto one canonical post (keeping
|
||||
the native post-id key), moving images, attachments and links onto the
|
||||
survivor. Images themselves are never touched.
|
||||
<div v-if="dupPreview">
|
||||
<p class="text-body-2 mb-2">
|
||||
<strong>{{ dupPreview.groups }}</strong> duplicate group(s),
|
||||
<strong>{{ dupPreview.posts_to_merge }}</strong> redundant row(s) to merge.
|
||||
<span v-if="dupPreview.groups > 50" class="fc-muted">Showing first 50.</span>
|
||||
<span v-else-if="!dupPreview.groups" class="fc-muted">No duplicates found.</span>
|
||||
</p>
|
||||
|
||||
<SampleNameGrid
|
||||
v-if="dupSampleNames.length"
|
||||
:names="dupSampleNames" class="mb-3"
|
||||
/>
|
||||
<v-btn
|
||||
color="accent" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-magnify"
|
||||
:loading="loadingDupPreview"
|
||||
class="mb-3"
|
||||
@click="onPreviewDupes"
|
||||
>Preview duplicate posts</v-btn>
|
||||
|
||||
<div v-if="dupPreview">
|
||||
<p class="text-body-2 mb-2">
|
||||
<strong>{{ dupPreview.groups }}</strong> duplicate group(s),
|
||||
<strong>{{ dupPreview.posts_to_merge }}</strong> redundant row(s) to
|
||||
merge.
|
||||
<span v-if="dupPreview.groups > 50" class="fc-muted">Showing first 50.</span>
|
||||
<span v-else-if="!dupPreview.groups" class="fc-muted">No duplicates found.</span>
|
||||
</p>
|
||||
<SampleNameGrid
|
||||
v-if="dupSampleNames.length"
|
||||
:names="dupSampleNames" class="mb-3"
|
||||
/>
|
||||
<v-btn
|
||||
color="error" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-merge"
|
||||
:disabled="!dupPreview.groups"
|
||||
:loading="merging"
|
||||
@click="onMergeDupes"
|
||||
>Merge {{ dupPreview.posts_to_merge }} duplicate(s)</v-btn>
|
||||
<span
|
||||
v-if="merged != null"
|
||||
class="ml-3 text-caption text-success"
|
||||
>Merged {{ merged }} ✓</span>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
color="error" variant="flat" rounded="pill"
|
||||
prepend-icon="mdi-merge"
|
||||
:disabled="!dupPreview.groups"
|
||||
:loading="merging"
|
||||
@click="onMergeDupes"
|
||||
>Merge {{ dupPreview.posts_to_merge }} duplicate(s)</v-btn>
|
||||
<span v-if="merged != null" class="ml-3 text-caption text-success">
|
||||
Merged {{ merged }} ✓
|
||||
</span>
|
||||
</div>
|
||||
</MaintenanceTile>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -102,7 +100,7 @@ import { computed, ref } from 'vue'
|
||||
|
||||
import { useAdminStore } from '../../stores/admin.js'
|
||||
import SampleNameGrid from '../common/SampleNameGrid.vue'
|
||||
import CardHeading from '../common/CardHeading.vue'
|
||||
import MaintenanceTile from '../common/MaintenanceTile.vue'
|
||||
|
||||
const store = useAdminStore()
|
||||
const preview = ref(null)
|
||||
@@ -165,7 +163,3 @@ async function onMergeDupes() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fc-post-maint { border-radius: 8px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user