bdf9cupe3: Add expert text search and category browse to the left column

This commit is contained in:
2026-10-19 17:33:43 -06:00
parent b97a77471b
commit 5b70bbfefa
7 changed files with 463 additions and 2 deletions
+21
View File
@@ -0,0 +1,21 @@
import { NextResponse } from "next/server";
import { browseCategory, searchExperts } from "@/lib/experts-search";
// GET /api/experts/search?q=<text> or ?category=<name>
// Same-origin proxy for Minnect's PUBLIC expert list. EXPERTS ONLY: it never
// calls a user-search endpoint. Public fields only. No session needed.
// Upstream failure -> 502 { error: "experts_unavailable" }.
export const runtime = "nodejs";
export async function GET(req: Request) {
const url = new URL(req.url);
const q = (url.searchParams.get("q") ?? "").trim().slice(0, 80);
const category = (url.searchParams.get("category") ?? "").trim().slice(0, 80);
if (!q && !category) return NextResponse.json({ experts: [] });
const result = q ? await searchExperts(q) : await browseCategory(category);
if (!result.ok) {
return NextResponse.json({ error: result.error }, { status: result.status });
}
return NextResponse.json({ experts: result.experts });
}
+16 -2
View File
@@ -83,7 +83,7 @@ button:disabled { opacity: .5; cursor: not-allowed; }
/* 3-column signed-in layout: nav | feed | rails. Wraps on narrow widths. */
.columns { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; max-width: 1240px; margin: 0 auto; padding: 0 16px; }
.col-left { flex: 0 0 180px; position: sticky; top: 72px; padding-top: 16px; }
.col-left ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.col-left > ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.col-left a { display: block; padding: 10px 12px; border-radius: 8px; color: var(--ink); text-decoration: none; font-weight: 500; }
.col-left a[aria-current="page"] { background: var(--ink); color: #fff; }
.col-center { flex: 1 1 400px; min-width: 0; }
@@ -105,9 +105,23 @@ button:disabled { opacity: .5; cursor: not-allowed; }
.minnect-audio { width: 100%; height: 36px; }
@media (max-width: 959px) {
.col-left { flex: 1 1 100%; position: static; padding-top: 8px; }
.col-left ul { flex-direction: row; }
.col-left > ul { flex-direction: row; }
.col-center { flex: 1 1 100%; order: 3; }
.col-right { flex: 1 1 100%; order: 2; position: static; max-height: none; overflow: visible; padding-top: 0; }
.col-right .rail-list { flex-direction: row; overflow-x: auto; padding-bottom: 8px; }
.col-right .rail-card { flex: 0 0 260px; }
}
/* Expert search + category chips (left column). Squared corners, monochrome + red. */
.expert-search { margin-top: 16px; }
.expert-search summary { font-weight: 700; font-size: 16px; cursor: pointer; padding: 4px 0; }
.expert-search form { display: flex; flex-direction: column; }
.expert-search input { border-radius: 0; width: 100%; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.chip { margin: 0; padding: 6px 10px; border-radius: 0; background: #fff; color: var(--ink); border: 1px solid var(--ink); font-size: 13px; }
.chip[aria-pressed="true"] { background: var(--red); border-color: var(--red); color: #fff; }
.search-results { margin-top: 12px; }
.search-note { margin: 0; font-size: 14px; color: var(--grey); }
@media (max-width: 959px) {
.col-left .expert-search { flex-basis: 100%; }
}