diff --git a/frontend/src/stores/seriesSuggestions.js b/frontend/src/stores/seriesSuggestions.js new file mode 100644 index 0000000..281c666 --- /dev/null +++ b/frontend/src/stores/seriesSuggestions.js @@ -0,0 +1,75 @@ +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 + } +}) diff --git a/frontend/src/views/SeriesView.vue b/frontend/src/views/SeriesView.vue index c135848..cb3e754 100644 --- a/frontend/src/views/SeriesView.vue +++ b/frontend/src/views/SeriesView.vue @@ -1,77 +1,154 @@