feat(wiki): add batch tag remove mode to admin wiki page

Add add/remove toggle to batch tag picker. Clean up unused requireAdmin
import from wiki sync route.
This commit is contained in:
Jennie Robinson Faber 2026-04-09 23:52:00 +01:00
parent a516f172fb
commit 50a358b294
3 changed files with 25 additions and 17 deletions

View file

@ -58,13 +58,17 @@
Select all in "{{ collectionFilter }}" Select all in "{{ collectionFilter }}"
</button> </button>
<div class="batch-tag-picker"> <div class="batch-tag-picker">
<select v-model="batchTagToAdd" aria-label="Tag to add" class="batch-select"> <select v-model="batchTagMode" aria-label="Batch tag mode" class="batch-select batch-mode-select">
<option value="">Add tag...</option> <option value="add">Add</option>
<option value="remove">Remove</option>
</select>
<select v-model="batchTagSelected" :aria-label="batchTagMode === 'add' ? 'Tag to add' : 'Tag to remove'" class="batch-select">
<option value="">Select tag...</option>
<option v-for="tag in availableTags" :key="tag.slug" :value="tag.slug">{{ tag.label }}</option> <option v-for="tag in availableTags" :key="tag.slug" :value="tag.slug">{{ tag.label }}</option>
</select> </select>
<button <button
class="btn btn-primary" class="btn btn-primary"
:disabled="!batchTagToAdd || batchApplying" :disabled="!batchTagSelected || batchApplying"
@click="applyBatchTag" @click="applyBatchTag"
> >
{{ batchApplying ? 'Applying...' : 'Apply' }} {{ batchApplying ? 'Applying...' : 'Apply' }}
@ -402,27 +406,31 @@ const saveArticleTags = async () => {
} }
// ---- Batch Tagging ---- // ---- Batch Tagging ----
const batchTagToAdd = ref('') const batchTagMode = ref('add')
const batchTagSelected = ref('')
const batchApplying = ref(false) const batchApplying = ref(false)
const applyBatchTag = async () => { const applyBatchTag = async () => {
if (!batchTagToAdd.value || !selectedIds.value.length) return if (!batchTagSelected.value || !selectedIds.value.length) return
batchApplying.value = true batchApplying.value = true
try { try {
const body = { articleIds: selectedIds.value }
if (batchTagMode.value === 'add') {
body.addTags = [batchTagSelected.value]
} else {
body.removeTags = [batchTagSelected.value]
}
const result = await $fetch('/api/admin/wiki/batch-tag', { const result = await $fetch('/api/admin/wiki/batch-tag', {
method: 'POST', method: 'POST',
body: { body,
articleIds: selectedIds.value,
addTags: [batchTagToAdd.value],
},
}) })
await refresh() await refresh()
const action = batchTagMode.value === 'add' ? 'added to' : 'removed from'
toast.add({ toast.add({
title: 'Batch tag applied', title: `Tag ${action} ${result.modified} articles`,
description: `${result.modified} articles updated`,
color: 'green', color: 'green',
}) })
batchTagToAdd.value = '' batchTagSelected.value = ''
selectedIds.value = [] selectedIds.value = []
} catch (err) { } catch (err) {
toast.add({ toast.add({
@ -553,6 +561,11 @@ const applyBatchTag = async () => {
outline: none; outline: none;
} }
.batch-mode-select {
width: auto;
min-width: 80px;
}
/* ---- TABLE ---- */ /* ---- TABLE ---- */
.table-wrap { .table-wrap {
padding: 0 28px 24px; padding: 0 28px 24px;

View file

@ -1,6 +1,5 @@
import WikiArticle from '../../../models/wikiArticle.js' import WikiArticle from '../../../models/wikiArticle.js'
import { connectDB } from '../../../utils/mongoose.js' import { connectDB } from '../../../utils/mongoose.js'
import { requireAdmin } from '../../../utils/auth.js'
import { fetchAllDocuments, extractSummary } from '../../../utils/outline.js' import { fetchAllDocuments, extractSummary } from '../../../utils/outline.js'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {

View file

@ -8,16 +8,12 @@ vi.mock('../../../server/models/wikiArticle.js', () => ({
} }
})) }))
vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() })) vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
vi.mock('../../../server/utils/auth.js', () => ({
requireAdmin: vi.fn()
}))
vi.mock('../../../server/utils/outline.js', () => ({ vi.mock('../../../server/utils/outline.js', () => ({
fetchAllDocuments: vi.fn(), fetchAllDocuments: vi.fn(),
extractSummary: vi.fn((text) => text || '') extractSummary: vi.fn((text) => text || '')
})) }))
import WikiArticle from '../../../server/models/wikiArticle.js' import WikiArticle from '../../../server/models/wikiArticle.js'
import { requireAdmin } from '../../../server/utils/auth.js'
import { fetchAllDocuments, extractSummary } from '../../../server/utils/outline.js' import { fetchAllDocuments, extractSummary } from '../../../server/utils/outline.js'
import syncHandler from '../../../server/api/admin/wiki/sync.post.js' import syncHandler from '../../../server/api/admin/wiki/sync.post.js'
import { createMockEvent } from '../helpers/createMockEvent.js' import { createMockEvent } from '../helpers/createMockEvent.js'