()
| 94 | } |
| 95 | |
| 96 | function CategoryCounts() { |
| 97 | const extensions = useQuery(api.extensions.listAllApproved) |
| 98 | |
| 99 | const counts: Record<string, number> = {} |
| 100 | HOMEPAGE_CATEGORIES.forEach((type) => { |
| 101 | counts[type] = 0 |
| 102 | }) |
| 103 | |
| 104 | if (extensions) { |
| 105 | extensions.forEach((ext) => { |
| 106 | if (counts[ext.type] !== undefined) { |
| 107 | counts[ext.type]++ |
| 108 | } |
| 109 | }) |
| 110 | } |
| 111 | |
| 112 | return ( |
| 113 | <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> |
| 114 | {HOMEPAGE_CATEGORIES.map((type) => { |
| 115 | const typeInfo = EXTENSION_TYPES[type] |
| 116 | return ( |
| 117 | <Link |
| 118 | key={type} |
| 119 | href={`/search?type=${type}`} |
| 120 | className="group flex flex-col gap-2 rounded-xl border border-[var(--color-border-weak)] bg-[var(--color-bg-weak)] p-5 transition-all hover:border-[var(--color-border)] hover:bg-[var(--color-bg-weak-hover)] hover:shadow-sm" |
| 121 | > |
| 122 | <span className="text-sm font-medium text-[var(--color-text-strong)]"> |
| 123 | {typeInfo.labelPlural} |
| 124 | </span> |
| 125 | <span className="text-xs text-[var(--color-text-weak)]"> |
| 126 | {counts[type]} {counts[type] === 1 ? "extension" : "extensions"} |
| 127 | </span> |
| 128 | </Link> |
| 129 | ) |
| 130 | })} |
| 131 | </div> |
| 132 | ) |
| 133 | } |
| 134 | |
| 135 | export default function Home() { |
| 136 | return ( |
nothing calls this directly
no outgoing calls
no test coverage detected