Wiki Phase 4: full-text search + revision history
Final phase of the wiki upgrade (see WIKI_UPGRADE.md). Schema (additive): wiki_revisions table (per-save content snapshots). The FULLTEXT index on wiki_pages(title, body) shipped in Phase 1. Search: - MATCH ... AGAINST natural-language search over title + body, ordered by relevance - public: GET /public/wiki?q= (published only); admin: GET /admin/wiki?q= (all statuses) - public wiki index gains a search box; admin list gains a search field Revision history: - every create/update snapshots the page into wiki_revisions - admin endpoints: list revisions, get one, and restore (restore overwrites the page, rebuilds links, and appends a new revision — history stays append-only); logged as wiki.revision.restore - editor gains a History modal: revision list + word-level diff (jsdiff) of a chosen revision against the current page, with one-click restore Verified end-to-end: search matches body and title; two edits produce three revisions; diff renders added/removed words; restore reverts and records a new revision. No console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,11 @@ import WikiCategories from './WikiCategories.jsx'
|
||||
export default function WikiAdmin() {
|
||||
const [tick, setTick] = useState(0)
|
||||
const reload = useCallback(() => setTick((t) => t + 1), [])
|
||||
const { loading, error, data } = useAsync(() => api.admin.listWiki(), [tick])
|
||||
const [q, setQ] = useState('')
|
||||
const { loading, error, data } = useAsync(
|
||||
() => api.admin.listWiki(q.trim() ? `?q=${encodeURIComponent(q.trim())}` : ''),
|
||||
[tick, q],
|
||||
)
|
||||
const [editing, setEditing] = useState(null) // null | 'new' | slug
|
||||
const [managingCats, setManagingCats] = useState(false)
|
||||
const pages = data || []
|
||||
@@ -21,6 +25,14 @@ export default function WikiAdmin() {
|
||||
{pages.length} page{pages.length === 1 ? '' : 's'} · edit content and structure
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 10 }}>
|
||||
<input
|
||||
type="search"
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.target.value)}
|
||||
className="input"
|
||||
placeholder="Search pages…"
|
||||
style={{ width: 200 }}
|
||||
/>
|
||||
<button onClick={() => setManagingCats(true)} className="pill">
|
||||
Manage sections
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user