diff --git a/frontend/src/components/pages/KnowledgeBasePage.js b/frontend/src/components/pages/KnowledgeBasePage.js
index bae66ab..719e388 100644
--- a/frontend/src/components/pages/KnowledgeBasePage.js
+++ b/frontend/src/components/pages/KnowledgeBasePage.js
@@ -1,25 +1,484 @@
-import React from 'react';
-import { BookOpen } from 'lucide-react';
+// KnowledgeBasePage.js
+// Full-page knowledge base library — browse, search, filter, and read
+// articles inline. Upload and delete require editor/admin role.
+// Reuses existing KnowledgeBaseViewer and KnowledgeBaseModal components.
-export default function KnowledgeBasePage() {
- return (
-
onSelect(article)}
+ style={{
+ background: selected
+ ? `linear-gradient(135deg,rgba(16,185,129,0.1) 0%,rgba(15,23,42,0.98) 100%)`
+ : 'linear-gradient(135deg,rgba(30,41,59,0.95) 0%,rgba(15,23,42,0.98) 100%)',
+ border: `1.5px solid ${selected ? GREEN : 'rgba(16,185,129,0.12)'}`,
+ borderRadius: '0.5rem',
+ padding: '1rem',
+ cursor: 'pointer',
+ transition: 'all 0.15s',
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '0.5rem',
+ }}
+ onMouseEnter={e => { if (!selected) e.currentTarget.style.borderColor = 'rgba(16,185,129,0.35)'; }}
+ onMouseLeave={e => { if (!selected) e.currentTarget.style.borderColor = 'rgba(16,185,129,0.12)'; }}
+ >
+ {/* File type badge + delete button */}
+
+
+ {ext}
+
+ {canDelete && (
+
+ )}
+
+
+ {/* Title */}
+
+ {article.title}
+
+
+ {/* Description */}
+ {article.description && (
+
+ {article.description}
+
+ )}
+
+ {/* Footer — category + date */}
+
+
+ {article.category}
+
+
+ {article.file_size && (
+
+ {fmtSize(article.file_size)}
+
+ )}
+
+ {fmtDate(article.created_at)}
+
+
+
+
+ );
+}
+
+// ---------------------------------------------------------------------------
+// Empty state
+// ---------------------------------------------------------------------------
+function EmptyState({ hasFilter, onClear }) {
+ return (
+
+
+ {/* ── Page header ─────────────────────────────────────────── */}
+
+
+
+ Knowledge Base
+
+
+ {loading ? '…' : `${articles.length} article${articles.length !== 1 ? 's' : ''}`}
+ {articles.length > 0 && activeCategory !== 'All' && (
+
+ · {categoryCounts[activeCategory] || 0} in {activeCategory}
+
+ )}
+
+
+
+
+
+ {canWrite() && (
+
+ )}
+
+
+
+ {/* ── Search + category tabs ───────────────────────────────── */}
+
+
+ {/* Search */}
+
+
+ setSearch(e.target.value)}
+ placeholder="Search articles…"
+ style={{
+ paddingLeft: '2rem', paddingRight: search ? '2rem' : '0.625rem',
+ paddingTop: '0.4rem', paddingBottom: '0.4rem',
+ background: 'rgba(15,23,42,0.8)',
+ border: '1px solid rgba(16,185,129,0.2)',
+ borderRadius: '0.375rem', color: '#E2E8F0',
+ outline: 'none', fontFamily: 'monospace', fontSize: '0.75rem',
+ width: '220px',
+ }}
+ onFocus={e => e.target.style.borderColor = `${GREEN}60`}
+ onBlur={e => e.target.style.borderColor = 'rgba(16,185,129,0.2)'}
+ />
+ {search && (
+
+ )}
+
+
+ {/* Category tabs */}
+
+ {activeTabs.map(cat => {
+ const isActive = activeCategory === cat;
+ const color = cat === 'All' ? GREEN : catColor(cat);
+ return (
+
+ );
+ })}
+
+
+
+ {/* ── Error state ──────────────────────────────────────────── */}
+ {error && (
+
+
+ {error}
+
+
+ )}
+
+ {/* ── Loading state ────────────────────────────────────────── */}
+ {loading && (
+
+
+
+ )}
+
+ {/* ── Article grid ─────────────────────────────────────────── */}
+ {!loading && !error && (
+
+ {filtered.length === 0 ? (
+
+ ) : (
+ filtered.map(article => (
+
setSelected(selected?.id === a.id ? null : a)}
+ onDelete={handleDelete}
+ canDelete={canWrite()}
+ />
+ ))
+ )}
+
+ )}
+
+ {/* ── Inline viewer ────────────────────────────────────────── */}
+ {selected && (
+
+ setSelected(null)}
+ />
+
+ )}
+
+ {/* ── Upload modal ─────────────────────────────────────────── */}
+ {showUpload && (
+
setShowUpload(false)}
+ onUpdate={() => { fetchArticles(); setShowUpload(false); }}
+ />
+ )}
+
+ );
}