/** * Theme Marketplace Browse * * Visual-first grid of theme cards with large thumbnails. * Navigates to theme detail on card click. */ import { Button, Input, Select } from "@cloudflare/kumo"; import type { MessageDescriptor } from "@lingui/core"; import { msg } from "@lingui/core/macro"; import { useLingui } from "@lingui/react/macro"; import { MagnifyingGlass, Palette, Warning, ArrowsClockwise, ArrowSquareOut, Eye, ShieldCheck, } from "@phosphor-icons/react"; import { useInfiniteQuery, useMutation } from "@tanstack/react-query"; import { Link } from "@tanstack/react-router"; import * as React from "react"; import { searchThemes, generatePreviewUrl, type ThemeSummary, type ThemeSearchOpts, } from "../lib/api/theme-marketplace.js"; type SortOption = "updated" | "created" | "name"; const SORT_LABELS: Record = { updated: msg`Recently Updated`, created: msg`Newest`, name: msg`Name`, }; const VALID_SORTS = new Set(["updated", "created", "name"]); function isSortOption(value: string): value is SortOption { return VALID_SORTS.has(value); } export function ThemeMarketplaceBrowse() { const { t } = useLingui(); const [searchQuery, setSearchQuery] = React.useState(""); const [sort, setSort] = React.useState("updated"); const [debouncedQuery, setDebouncedQuery] = React.useState(""); React.useEffect(() => { const timer = setTimeout(setDebouncedQuery, 300, searchQuery); return () => clearTimeout(timer); }, [searchQuery]); const searchOpts: ThemeSearchOpts = { q: debouncedQuery || undefined, sort, limit: 12, }; const { data, isLoading, error, refetch, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({ queryKey: ["themes", "search", searchOpts], queryFn: ({ pageParam }) => searchThemes({ ...searchOpts, cursor: pageParam }), initialPageParam: undefined as string | undefined, getNextPageParam: (lastPage) => lastPage.nextCursor, }); const themes = data?.pages.flatMap((p) => p.items); return (
{/* Header */}

{t`Themes`}

{t`Browse themes and preview them with your own content.`}

{/* Search + Sort */}
setSearchQuery(e.target.value)} className="ps-9" aria-label={t`Search themes`} />