feat(ui): "Reject rest" per suggestion category — confirm the good, reject the rest

Each category section header gets a subtle "Reject rest" action that dismisses
every still-unhandled suggestion in it at once (store.dismissRemaining, parallel
dispatch). Canonical tags persist a rejection and stay flagged (reversible,
one-click un-reject); raw creates-new-tag rows drop client-side. Shows only when
the section has unhandled items. No confirm dialog — it's fully reversible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
2026-07-06 18:42:08 -04:00
parent 2d44a26bdf
commit 6684907577
3 changed files with 83 additions and 15 deletions
@@ -25,6 +25,7 @@
collapsible :default-open="true"
@accept="onAccept" @alias="onAlias" @remove-alias="onRemoveAlias"
@dismiss="onDismiss" @undismiss="onUndismiss"
@reject-all="onRejectAll('system')"
/>
<SuggestionsCategoryGroup
v-for="cat in peopleCats" :key="cat"
@@ -32,6 +33,7 @@
:label="labelFor(cat)" :items="store.byCategory[cat] || []"
@accept="onAccept" @alias="onAlias" @remove-alias="onRemoveAlias"
@dismiss="onDismiss" @undismiss="onUndismiss"
@reject-all="onRejectAll(cat)"
/>
<SuggestionsCategoryGroup
v-if="store.byCategory.general && store.byCategory.general.length"
@@ -39,6 +41,7 @@
collapsible :default-open="true"
@accept="onAccept" @alias="onAlias" @remove-alias="onRemoveAlias"
@dismiss="onDismiss" @undismiss="onUndismiss"
@reject-all="onRejectAll('general')"
/>
</div>
@@ -76,6 +79,16 @@ const emit = defineEmits(['accepted', 'dismissed'])
// re-focus the tag input — same return-to-input behaviour as accept.
function onDismiss (s) { store.dismiss(s); emit('dismissed') }
function onUndismiss (s) { store.undismiss(s); emit('dismissed') }
// Section-level "Reject rest": dismiss every still-unhandled suggestion in the
// category, then return focus to the input like a single reject does.
async function onRejectAll (category) {
try {
await store.dismissRemaining(category)
emit('dismissed')
} catch (e) {
toast({ text: `Reject failed: ${e.message}`, type: 'error' })
}
}
const store = useSuggestionsStore()
const modalStore = useModalStore()
const host = props.host || modalStore