feat(#67): implement search UI with filters and shortcuts

Implements comprehensive search interface for knowledge base:

Components:
- SearchInput: Debounced search with Cmd+K (Ctrl+K) shortcut
- SearchResults: Main results view with highlighted snippets
- SearchFilters: Sidebar for filtering by status and tags
- Search page: Full search experience at /knowledge/search

Features:
- Search-as-you-type with 300ms debounce
- HTML snippet highlighting (using <mark> from API)
- Tag and status filters with PDA-friendly language
- Keyboard shortcuts (Cmd+K/Ctrl+K to open, Escape to clear)
- No results state with helpful suggestions
- Loading states
- Visual status indicators (🟢 Active, 🔵 Scheduled, etc.)

Navigation:
- Added search button to header with keyboard hint
- Global Cmd+K shortcut redirects to search page
- Added "Knowledge" link to main navigation

Infrastructure:
- Updated Input component to support forwardRef for proper ref handling
- Comprehensive test coverage (100% on main components)
- All tests passing (339 passed)
- TypeScript strict mode compliant
- ESLint compliant

Fixes #67

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-02 14:50:25 -06:00
parent c3500783d1
commit 3cb6eb7f8b
12 changed files with 1221 additions and 11 deletions

View File

@@ -1,3 +1,4 @@
import { forwardRef } from "react";
import type { InputHTMLAttributes, ReactElement } from "react";
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
@@ -7,15 +8,10 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
fullWidth?: boolean;
}
export function Input({
label,
error,
helperText,
fullWidth = false,
className = "",
id,
...props
}: InputProps): ReactElement {
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
{ label, error, helperText, fullWidth = false, className = "", id, ...props },
ref
): ReactElement {
const inputId = id ?? `input-${Math.random().toString(36).substring(2, 11)}`;
const errorId = error ? `${inputId}-error` : undefined;
const helperId = helperText ? `${inputId}-helper` : undefined;
@@ -37,6 +33,7 @@ export function Input({
</label>
)}
<input
ref={ref}
id={inputId}
className={combinedClassName}
aria-invalid={error ? "true" : "false"}
@@ -55,4 +52,4 @@ export function Input({
)}
</div>
);
}
});