From 823da1d143df12640064857e76a37b8165e0e4ef Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Sat, 14 Feb 2026 13:09:48 +0530 Subject: [PATCH] feat(skills): improve marketplace filters and server pagination --- backend/app/api/skills_marketplace.py | 5 +- frontend/src/app/skills/marketplace/page.tsx | 67 +++++++++++-------- .../skills/MarketplaceSkillsTable.tsx | 5 +- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/backend/app/api/skills_marketplace.py b/backend/app/api/skills_marketplace.py index 53df8462..32243559 100644 --- a/backend/app/api/skills_marketplace.py +++ b/backend/app/api/skills_marketplace.py @@ -717,7 +717,7 @@ def _as_card( skill: MarketplaceSkill, installation: GatewayInstalledSkill | None, ) -> MarketplaceSkillCardRead: - card_source = skill.source_url + card_source: str | None = skill.source_url if not card_source: card_source = skill.source @@ -966,8 +966,7 @@ async def list_marketplace_skills( ) else: skills_query = skills_query.filter( - func.lower(func.trim(col(MarketplaceSkill.category))) - == normalized_category, + func.lower(func.trim(col(MarketplaceSkill.category))) == normalized_category, ) normalized_risk = (risk or "").strip().lower() diff --git a/frontend/src/app/skills/marketplace/page.tsx b/frontend/src/app/skills/marketplace/page.tsx index 98b378a0..95d1f3c3 100644 --- a/frontend/src/app/skills/marketplace/page.tsx +++ b/frontend/src/app/skills/marketplace/page.tsx @@ -177,7 +177,9 @@ export default function SkillsMarketplacePage() { const [selectedCategory, setSelectedCategory] = useState( initialCategory || "all", ); - const [selectedRisk, setSelectedRisk] = useState(initialRisk || "safe"); + const [selectedRisk, setSelectedRisk] = useState( + initialRisk || "safe", + ); const [currentPage, setCurrentPage] = useState(initialPage); const [pageSize, setPageSize] = useState(initialPageSize); @@ -261,34 +263,29 @@ export default function SkillsMarketplacePage() { const skillsQuery = useListMarketplaceSkillsApiV1SkillsMarketplaceGet< listMarketplaceSkillsApiV1SkillsMarketplaceGetResponse, ApiError - >( - skillsParams, - { - query: { - enabled: Boolean(isSignedIn && isAdmin && resolvedGatewayId), - refetchOnMount: "always", - refetchInterval: 15_000, - }, + >(skillsParams, { + query: { + enabled: Boolean(isSignedIn && isAdmin && resolvedGatewayId), + refetchOnMount: "always", + refetchInterval: 15_000, }, - ); + }); const skills = useMemo( () => (skillsQuery.data?.status === 200 ? skillsQuery.data.data : []), [skillsQuery.data], ); - const filterOptionSkillsQuery = useListMarketplaceSkillsApiV1SkillsMarketplaceGet< - listMarketplaceSkillsApiV1SkillsMarketplaceGetResponse, - ApiError - >( - filterOptionsParams, - { + const filterOptionSkillsQuery = + useListMarketplaceSkillsApiV1SkillsMarketplaceGet< + listMarketplaceSkillsApiV1SkillsMarketplaceGetResponse, + ApiError + >(filterOptionsParams, { query: { enabled: Boolean(isSignedIn && isAdmin && resolvedGatewayId), refetchOnMount: "always", refetchInterval: 15_000, }, - }, - ); + }); const filterOptionSkills = useMemo( () => filterOptionSkillsQuery.data?.status === 200 @@ -322,7 +319,10 @@ export default function SkillsMarketplacePage() { return { hasKnownTotal: false, total: skills.length }; } const totalCountHeader = skillsQuery.data.headers.get("x-total-count"); - if (typeof totalCountHeader === "string" && totalCountHeader.trim() !== "") { + if ( + typeof totalCountHeader === "string" && + totalCountHeader.trim() !== "" + ) { const parsed = Number(totalCountHeader); if (Number.isFinite(parsed) && parsed >= 0) { return { hasKnownTotal: true, total: parsed }; @@ -345,13 +345,16 @@ export default function SkillsMarketplacePage() { return currentPage < totalPages; } return skills.length === pageSize; - }, [currentPage, pageSize, skills.length, totalCountInfo.hasKnownTotal, totalPages]); - const rangeStart = - totalSkills === 0 ? 0 : (currentPage - 1) * pageSize + 1; + }, [ + currentPage, + pageSize, + skills.length, + totalCountInfo.hasKnownTotal, + totalPages, + ]); + const rangeStart = totalSkills === 0 ? 0 : (currentPage - 1) * pageSize + 1; const rangeEnd = - totalSkills === 0 - ? 0 - : (currentPage - 1) * pageSize + skills.length; + totalSkills === 0 ? 0 : (currentPage - 1) * pageSize + skills.length; const categoryFilterOptions = useMemo(() => { const byValue = new Map(); @@ -819,7 +822,10 @@ export default function SkillsMarketplacePage() { All categories {categoryFilterOptions.map((category) => ( - + {category.label} ))} @@ -833,7 +839,10 @@ export default function SkillsMarketplacePage() { > Risk - setCurrentPage((prev) => Math.max(1, prev - 1))} + onClick={() => + setCurrentPage((prev) => Math.max(1, prev - 1)) + } > Previous diff --git a/frontend/src/components/skills/MarketplaceSkillsTable.tsx b/frontend/src/components/skills/MarketplaceSkillsTable.tsx index 112de01e..9e5b8aa4 100644 --- a/frontend/src/components/skills/MarketplaceSkillsTable.tsx +++ b/frontend/src/components/skills/MarketplaceSkillsTable.tsx @@ -80,7 +80,10 @@ function riskBadgeLabel(risk: string | null | undefined) { type MarketplaceSkillsTableProps = { skills: MarketplaceSkillCardRead[]; - installedGatewayNamesBySkillId?: Record; + installedGatewayNamesBySkillId?: Record< + string, + { id: string; name: string }[] + >; isLoading?: boolean; sorting?: SortingState; onSortingChange?: OnChangeFn;