"use client"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { Suspense, useCallback, useEffect, useMemo, useState } from "react"; import { AppShell } from "@/components/AppShell"; import { searchNoteSummaries } from "@/lib/notes-client"; import { listSavedViews, createSavedView, deleteSavedView, type SavedView } from "@/lib/saved-views-client"; import { useDebounce } from "@/lib/use-debounce"; import type { NoteSummary } from "@/lib/types"; export default function SearchPage() { return (

Loading...

}>
); } function SearchPageInner() { const searchParams = useSearchParams(); const [notes, setNotes] = useState([]); const [query, setQuery] = useState(() => searchParams?.get("q") ?? ""); const debouncedQuery = useDebounce(query, 250); const [savedViewsList, setSavedViewsList] = useState([]); const [error, setError] = useState(null); useEffect(() => { setQuery(searchParams?.get("q") ?? ""); }, [searchParams]); useEffect(() => { void (async () => { try { setNotes(await searchNoteSummaries(debouncedQuery)); setError(null); } catch (err) { setError(err instanceof Error ? err.message : "Unable to load notes"); } })(); }, [debouncedQuery]); useEffect(() => { void (async () => { try { setSavedViewsList(await listSavedViews("search")); } catch { // Saved views are best-effort } })(); }, []); const handleSaveCurrentSearch = useCallback(async () => { if (!query.trim()) return; try { const view = await createSavedView({ id: crypto.randomUUID(), name: query.trim().slice(0, 60), scope: "search", query: query.trim(), }); setSavedViewsList((current) => [...current, view]); } catch { // Best-effort } }, [query]); const handleDeleteSavedView = useCallback(async (id: string) => { try { await deleteSavedView(id); setSavedViewsList((current) => current.filter((v) => v.id !== id)); } catch { // Best-effort } }, []); const savedViews = useMemo(() => savedViewsList.map((view) => ({ id: view.id, name: view.name, query: view.query, resultCount: 0, })), [savedViewsList]); return ( Backend-backed note search} >
setQuery(event.target.value)} />
workspace:all status:active source:manual+agent matched:title+body
{error ?
{error}
: null}
{notes.map((note) => (
{note.title} {note.excerpt} {note.status} {note.updatedBy} {note.workspaceId.replace("workspace-", "")}
{note.tags.map((tag) => ( #{tag} ))}
))}
); }