From 7a5c5f28827e0cef65528b13375e038b8bab7643 Mon Sep 17 00:00:00 2001 From: Justin Paul Date: Tue, 9 Jun 2026 09:34:20 -0400 Subject: [PATCH] Visibility phase 5: public /explore directory + search A no-login directory of shared trees, backed by GET /api/v1/public/trees: - /explore: searchable grid of public trees; debounced name search. Because the backend adds `site_members` trees when a valid session is present, signed-in users see more with no client-side branching. - PublicHeader extracted and shared by /p and /explore (logo, Explore, Sign in). - "Explore" entry added to the authed sidebar. tsc clean; next build passes. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Justin Paul --- frontend/app/explore/layout.tsx | 10 ++++ frontend/app/explore/page.tsx | 78 +++++++++++++++++++++++++++ frontend/app/p/layout.tsx | 14 +---- frontend/components/app-sidebar.tsx | 2 + frontend/components/public-header.tsx | 23 ++++++++ 5 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 frontend/app/explore/layout.tsx create mode 100644 frontend/app/explore/page.tsx create mode 100644 frontend/components/public-header.tsx diff --git a/frontend/app/explore/layout.tsx b/frontend/app/explore/layout.tsx new file mode 100644 index 0000000..081f266 --- /dev/null +++ b/frontend/app/explore/layout.tsx @@ -0,0 +1,10 @@ +import { PublicHeader } from "@/components/public-header"; + +export default function ExploreLayout({ children }: { children: React.ReactNode }) { + return ( +
+ +
{children}
+
+ ); +} diff --git a/frontend/app/explore/page.tsx b/frontend/app/explore/page.tsx new file mode 100644 index 0000000..53e53e2 --- /dev/null +++ b/frontend/app/explore/page.tsx @@ -0,0 +1,78 @@ +"use client"; + +import Link from "next/link"; +import { useEffect, useState } from "react"; + +import { api } from "@/lib/api/client"; +import type { components } from "@/lib/api/schema"; +import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; + +type Tree = components["schemas"]["PublicTreeRead"]; + +// Public directory of trees. The backend returns `public` to everyone and adds +// `site_members` trees when the request carries a valid session — so signed-in +// users see more here without any client-side branching. +export default function ExplorePage() { + const [trees, setTrees] = useState([]); + const [search, setSearch] = useState(""); + const [ready, setReady] = useState(false); + + useEffect(() => { + const q = search.trim(); + const t = setTimeout(async () => { + const { data } = await api.GET("/api/v1/public/trees", { + params: { query: q ? { q } : {} }, + }); + setTrees(data ?? []); + setReady(true); + }, 200); + return () => clearTimeout(t); + }, [search]); + + return ( +
+
+

Explore public trees

+

+ Browse family trees shared on this site. Living people are always hidden. +

+
+ + setSearch(e.target.value)} + /> + + {!ready ? ( +

Loading…

+ ) : trees.length === 0 ? ( +

No public trees{search.trim() ? " match that search" : " yet"}.

+ ) : ( +
    + {trees.map((t) => ( +
  • + + + +
    + {t.name} + + {t.visibility === "site_members" ? "Members" : "Public"} + +
    + {t.description && ( +

    {t.description}

    + )} +
    +
    + +
  • + ))} +
+ )} +
+ ); +} diff --git a/frontend/app/p/layout.tsx b/frontend/app/p/layout.tsx index 571d26b..e03a460 100644 --- a/frontend/app/p/layout.tsx +++ b/frontend/app/p/layout.tsx @@ -1,20 +1,10 @@ -import Link from "next/link"; +import { PublicHeader } from "@/components/public-header"; // Public viewing surface — no auth, no app sidebar. A slim header only. export default function PublicLayout({ children }: { children: React.ReactNode }) { return (
-
-
- - {/* eslint-disable-next-line @next/next/no-img-element */} - Provenance - - - Sign in - -
-
+
{children}
); diff --git a/frontend/components/app-sidebar.tsx b/frontend/components/app-sidebar.tsx index aeb662a..2f4a96d 100644 --- a/frontend/components/app-sidebar.tsx +++ b/frontend/components/app-sidebar.tsx @@ -4,6 +4,7 @@ import { Archive, ArrowDownUp, BookText, + Compass, FolderTree, Image as ImageIcon, LogOut, @@ -92,6 +93,7 @@ export function AppSidebar({ onNavigate }: { onNavigate?: () => void }) { + {treeId && ( diff --git a/frontend/components/public-header.tsx b/frontend/components/public-header.tsx new file mode 100644 index 0000000..ba8361b --- /dev/null +++ b/frontend/components/public-header.tsx @@ -0,0 +1,23 @@ +import Link from "next/link"; + +// Slim header for the public (no-auth) surface: /p/* and /explore. +export function PublicHeader() { + return ( +
+
+ + {/* eslint-disable-next-line @next/next/no-img-element */} + Provenance + + +
+
+ ); +} -- 2.52.0