Files
FabledCurator/frontend/src/stores/seriesSuggestions.js
T
bvandeusen 19a91a1641
CI / frontend-build (push) Successful in 19s
CI / lint (push) Successful in 2s
CI / backend-lint-and-test (push) Successful in 25s
CI / integration (push) Successful in 3m7s
feat(series): Suggestions tab + matcher controls — frontend (FC-6.3)
Completes FC-6.3 with the UI.

- SeriesView gains tabs: Browse (the existing grid) + Suggestions.
- Suggestions tab: pending matches as rows (post → series, per-signal strength
  chips, score), Add (→ chapter) / Skip (→ dismiss); a "Matching on" toggle and
  a threshold field (both DB-backed via /settings/import), and a Rescan button
  that enqueues the background matcher.
- seriesSuggestions store wires load / accept / dismiss / rescan / settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:02:19 -04:00

76 lines
2.3 KiB
JavaScript

import { defineStore } from 'pinia'
import { ref } from 'vue'
import { useApi } from '../composables/useApi.js'
import { useAsyncAction } from '../composables/useAsyncAction.js'
import { toast } from '../utils/toast.js'
// Backs the Series → Suggestions tab (FC-6.3). The matcher is confirm-only:
// accept turns a suggestion into a chapter; skip dismisses it. enabled +
// threshold are the operator-tunable knobs (DB-backed via /settings/import).
export const useSeriesSuggestionsStore = defineStore('seriesSuggestions', () => {
const api = useApi()
const suggestions = ref([])
const enabled = ref(true)
const threshold = ref(0.5)
const { loading, error, run } = useAsyncAction({ errorAs: 'message' })
async function load() {
await run(async () => {
const body = await api.get('/api/series/suggestions')
suggestions.value = body.suggestions || []
})
}
async function loadSettings() {
const s = await api.get('/api/settings/import')
enabled.value = s.series_suggest_enabled
threshold.value = s.series_suggest_threshold
}
async function saveSettings(patch) {
await api.patch('/api/settings/import', { body: patch })
}
async function setEnabled(v) {
enabled.value = v
await saveSettings({ series_suggest_enabled: v })
}
async function setThreshold(v) {
threshold.value = v
await saveSettings({ series_suggest_threshold: v })
}
async function accept(id) {
try {
await api.post(`/api/series/suggestions/${id}/accept`, {})
suggestions.value = suggestions.value.filter(s => s.id !== id)
toast({ text: 'Added to series', type: 'success' })
} catch (e) {
toast({ text: `Accept failed: ${e.message}`, type: 'error' })
}
}
async function dismiss(id) {
try {
await api.post(`/api/series/suggestions/${id}/dismiss`, {})
suggestions.value = suggestions.value.filter(s => s.id !== id)
} catch (e) {
toast({ text: `Skip failed: ${e.message}`, type: 'error' })
}
}
async function rescan() {
await api.post('/api/series/suggestions/rescan', {})
toast({
text: 'Rescan queued — runs in the background; reload to see new matches.',
type: 'info'
})
}
return {
suggestions, enabled, threshold, loading, error,
load, loadSettings, setEnabled, setThreshold, accept, dismiss, rescan
}
})