From f29efb792410874bb5a936b80be7ccc88393efc4 Mon Sep 17 00:00:00 2001 From: jmartin Date: Fri, 24 Jul 2026 11:19:47 -0400 Subject: [PATCH] Hard-filter /portfolio/:slug when it matches a category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Category chips (e.g. /portfolio/photography) now show only that category — empty if none. Audience views like /portfolio/game-dev still use prioritization. --- client/src/lib/api.ts | 4 +++ client/src/pages/PortfolioPage.tsx | 56 ++++++++++++++++++++++++------ server/src/routes/public.ts | 46 ++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index e80588b..9e7c849 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -105,12 +105,16 @@ export interface ListMeta { perPage: number; total: number; totalPages: number; + /** all | category (hard filter) | view (audience prioritization) */ + mode?: "all" | "category" | "view"; + category?: Category; view?: { slug: string; name: string; description?: string | null; categories?: Category[]; }; + search?: boolean; } async function request(path: string, init?: RequestInit): Promise { diff --git a/client/src/pages/PortfolioPage.tsx b/client/src/pages/PortfolioPage.tsx index 469d554..1362205 100644 --- a/client/src/pages/PortfolioPage.tsx +++ b/client/src/pages/PortfolioPage.tsx @@ -53,13 +53,21 @@ export function PortfolioPage() { setSearchParams(next); } - const title = meta?.view?.name || (category ? categories.find((c) => c.slug === category)?.name : null) || "Portfolio"; + const mode = meta?.mode; + const title = + meta?.view?.name || + meta?.category?.name || + (category ? categories.find((c) => c.slug === category)?.name : null) || + "Portfolio"; const description = meta?.view?.description || - "Browse projects by category, tag, or search. Share tailored views with clients and collaborators."; + meta?.category?.description || + (mode === "category" + ? `Only projects tagged ${title}.` + : "Browse projects by category, tag, or search. Share tailored views with clients and collaborators."); const tabTitle = viewSlug - ? meta?.view?.name || viewSlug + ? meta?.view?.name || meta?.category?.name || viewSlug : category ? categories.find((c) => c.slug === category)?.name || category : q @@ -67,6 +75,10 @@ export function PortfolioPage() { : "Portfolio"; usePageTitle(tabTitle); + const activeSlug = viewSlug || category; + const isCategoryMode = mode === "category" || (!!viewSlug && categories.some((c) => c.slug === viewSlug)); + const isViewMode = mode === "view"; + return (
@@ -74,11 +86,19 @@ export function PortfolioPage() {

Work

{title}

{description}

- {viewSlug && ( + {activeSlug && (

- Viewing curated list /portfolio/{viewSlug} + {isCategoryMode && !isViewMode ? ( + <> + Filtered by category /portfolio/{activeSlug} + + ) : ( + <> + Viewing curated list /portfolio/{activeSlug} + + )} {" · "} - clear view + show all

)}
@@ -96,10 +116,19 @@ export function PortfolioPage() { aria-label="Search projects" />