Files
FabledCurator/frontend/src/views/SettingsView.vue
T
bvandeusen 311fe0ee9c
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 26s
CI / backend-lint-and-test (push) Successful in 42s
CI / integration (push) Successful in 3m27s
feat(settings): tidy Maintenance tab into compact tiles + center the views (pass 2)
Goal (operator 2026-06-18): the overview of a Settings tab fits one unscrolled
viewport; expanding a tile to read into it is the only reason to scroll.

- Every Maintenance card converted to the collapsible MaintenanceTile (collapsed
  by default = icon + short title + one-line blurb). Task cards (ML backfill,
  centroids, thumbnails, archive re-extract, missing-file repair, DB maintenance)
  sit in a responsive grid; running tasks auto-expand. Tagging config (suggestion
  thresholds, allowlist, aliases) grouped in one Tagging section as collapsible
  tiles; Backup is its own collapsible tile.
- Three labeled sections mirror the Cleanup tab: Backfills and reprocessing /
  Tagging / Storage.
- Center the whole Settings surface: SettingsView is now a centered, width-capped
  (1140px) column so the tab strip and every panel sit in a tidy centered measure
  (was full-width). CleanupView drops its own left-aligned max-width to fill it.

All card logic unchanged - only the chrome.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 00:10:58 -04:00

139 lines
4.9 KiB
Vue

<template>
<!-- Centered, width-capped column so the tab strip + every panel sit in a
tidy centered measure rather than stretching full-width (operator
2026-06-18). v-container is margin-auto by default; the cap keeps long
maintenance/import panels readable. -->
<v-container class="pt-2 pb-6 fc-settings">
<!-- Sticky tabs: operator-flagged 2026-05-25 long Import / Maintenance
panels pushed the tab strip out of the viewport, forcing a scroll-
to-top just to change tab. AppShell's TopNav is 64px sticky, so the
tab strip lives directly under it. Background uses the theme surface
token so it visually merges with the page rather than the
translucent v-tabs default. -->
<v-tabs
v-model="tab" color="accent" class="mb-4"
style="position: sticky; top: 64px; z-index: 4;
background: rgb(var(--v-theme-surface));"
>
<v-tab value="overview">Overview</v-tab>
<v-tab value="activity">Activity</v-tab>
<v-tab value="import">Import</v-tab>
<v-tab value="cleanup">Cleanup</v-tab>
<v-tab value="maintenance">Maintenance</v-tab>
</v-tabs>
<v-window v-model="tab">
<v-window-item value="overview">
<SystemStatsCards :stats="system.stats" />
<SystemActivitySummary
class="mt-4"
@open-activity="tab = 'activity'"
/>
<v-alert
v-if="system.stats && (system.stats.tasks.pending + system.stats.tasks.queued) > 0"
type="info" variant="tonal" class="mt-4" closable
>
{{ system.stats.tasks.pending + system.stats.tasks.queued }} import task(s) pending.
<v-btn variant="text" size="small" @click="tab = 'import'">Go to Import tab</v-btn>
</v-alert>
<!-- Browser-extension install/download lives on Overview (moved
from Maintenance 2026-05-25). Overview is the discovery
surface for "things to set up"; Maintenance is for
housekeeping of already-set-up systems. -->
<BrowserExtensionCard class="mt-6" />
</v-window-item>
<v-window-item value="activity">
<SystemActivityTab />
</v-window-item>
<v-window-item value="import">
<!-- Order: filters → trigger → recent tasks. Filters hoisted above the
trigger (operator-flagged 2026-06-04); the task list stays
directly below the trigger so hit/miss feedback is adjacent to the
button that produced it (operator-flagged 2026-05-25). -->
<ImportFiltersForm />
<v-divider class="my-6" />
<ImportTriggerPanel />
<v-divider class="my-6" />
<ImportTaskList />
</v-window-item>
<v-window-item value="cleanup">
<CleanupView />
</v-window-item>
<v-window-item value="maintenance">
<MaintenancePanel />
</v-window-item>
</v-window>
</v-container>
</template>
<script setup>
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useSystemStore } from '../stores/system.js'
import { useImportStore } from '../stores/import.js'
import SystemStatsCards from '../components/settings/SystemStatsCards.vue'
import SystemActivitySummary from '../components/settings/SystemActivitySummary.vue'
import SystemActivityTab from '../components/settings/SystemActivityTab.vue'
import BrowserExtensionCard from '../components/settings/BrowserExtensionCard.vue'
import ImportTriggerPanel from '../components/settings/ImportTriggerPanel.vue'
import ImportFiltersForm from '../components/settings/ImportFiltersForm.vue'
import ImportTaskList from '../components/settings/ImportTaskList.vue'
import MaintenancePanel from '../components/settings/MaintenancePanel.vue'
import CleanupView from './CleanupView.vue'
import { useMLStore } from '../stores/ml.js'
const tab = ref('overview')
const system = useSystemStore()
const importStore = useImportStore()
const mlStore = useMLStore()
let pollId = null
function startPolling() {
if (pollId) return
system.refreshStats()
pollId = setInterval(() => {
if (!document.hidden) {
system.refreshStats()
if (tab.value === 'import') {
importStore.refreshStatus()
// Refresh the task list while a batch is in flight so the UI
// doesn't sit stale next to a ticking imported/skipped counter.
if (importStore.activeBatch) importStore.loadTasks(true)
}
}
}, 5000)
}
function stopPolling() {
if (pollId) { clearInterval(pollId); pollId = null }
}
onMounted(() => {
startPolling()
importStore.loadSettings()
importStore.refreshStatus()
importStore.loadTasks(true)
})
onUnmounted(stopPolling)
watch(tab, (t) => {
if (t === 'import') {
importStore.refreshStatus()
importStore.loadTasks(true)
} else if (t === 'maintenance') {
mlStore.loadSettings()
}
})
</script>
<style scoped>
/* Centered, capped measure for the whole Settings surface (tabs + panels). */
.fc-settings {
max-width: 1140px;
}
</style>