Skip to content

Commit a7968bf

Browse files
committed
feat(catalog): link product pages to manifests
1 parent f03b04c commit a7968bf

23 files changed

Lines changed: 114 additions & 4 deletions

File tree

src/app/[locale]/desktops/[slug]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export default async function DesktopPage({
9090
<main className="max-w-8xl mx-auto px-[var(--spacing-md)]">
9191
<ProductHero
9292
name={desktop.name}
93+
manifestId={desktop.id}
9394
description={desktop.description}
9495
vendor={desktop.vendor}
9596
category="DESKTOP"

src/app/[locale]/extensions/[slug]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export default async function ExtensionPage({
130130
<main className="max-w-8xl mx-auto px-[var(--spacing-md)]">
131131
<ProductHero
132132
name={extension.name}
133+
manifestId={extension.id}
133134
description={extension.description}
134135
vendor={extension.vendor}
135136
category="EXTENSION"

src/app/[locale]/ides/[slug]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export default async function IDEPage({
110110
<main className="max-w-8xl mx-auto px-[var(--spacing-md)]">
111111
<ProductHero
112112
name={ide.name}
113+
manifestId={ide.id}
113114
description={ide.description}
114115
vendor={ide.vendor}
115116
category="IDE"

src/app/[locale]/model-providers/[slug]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export default async function ProviderPage({
8383
<main className="max-w-8xl mx-auto px-[var(--spacing-md)]">
8484
<ProductHero
8585
name={provider.name}
86+
manifestId={provider.id}
8687
description={provider.description}
8788
category="PROVIDER"
8889
categoryLabel={tShared('categories.singular.modelProvider')}

src/app/[locale]/models/[slug]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export default async function ModelPage({
119119
<main className="max-w-8xl mx-auto px-[var(--spacing-md)]">
120120
<ProductHero
121121
name={model.name}
122+
manifestId={model.id}
122123
description={
123124
<>
124125
{tShared('terms.by')}{' '}

src/app/[locale]/vendors/[slug]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ export default async function VendorPage({
144144
<main className="max-w-8xl mx-auto px-[var(--spacing-md)]">
145145
<ProductHero
146146
name={vendor.name}
147+
manifestId={vendor.id}
147148
description={vendor.description}
148149
category="VENDOR"
149150
categoryLabel={tShared('categories.singular.vendor')}

src/components/product/CLILandingPage.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { ReactNode } from 'react'
1212
import { DeprecatedBadge } from '@/components/controls/DeprecatedBadge'
1313
import { VerifiedBadge } from '@/components/controls/VerifiedBadge'
1414
import { CLICommandPanel } from '@/components/product/CLICommandPanel'
15+
import { ManifestEditLink } from '@/components/product/ManifestEditLink'
1516
import { ProductPricing } from '@/components/product/ProductPricing'
1617
import { RelatedProducts, type RelatedProductsProps } from '@/components/product/RelatedProducts'
1718
import { Link } from '@/i18n/navigation'
@@ -120,9 +121,12 @@ export function CLILandingPage({
120121
{cli.deprecated && <DeprecatedBadge size="sm" />}
121122
</div>
122123

123-
<h1 className="text-5xl md:text-6xl font-semibold tracking-[-0.05em] detail-page-h1 mb-[var(--spacing-md)]">
124-
{cli.name}
125-
</h1>
124+
<div className="flex items-center gap-[var(--spacing-xs)] mb-[var(--spacing-md)]">
125+
<h1 className="text-5xl md:text-6xl font-semibold tracking-[-0.05em] detail-page-h1">
126+
{cli.name}
127+
</h1>
128+
<ManifestEditLink category="CLI" manifestId={cli.id} />
129+
</div>
126130
<p className="text-xl leading-relaxed font-medium max-w-2xl mb-[var(--spacing-sm)]">
127131
{content.answer}
128132
</p>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Github } from 'lucide-react'
2+
import { useTranslations } from 'next-intl'
3+
import { getManifestEditUrl, type ManifestCategory } from '@/lib/manifest-source'
4+
5+
export interface ManifestEditLinkProps {
6+
category: ManifestCategory
7+
manifestId: string
8+
}
9+
10+
export function ManifestEditLink({ category, manifestId }: ManifestEditLinkProps) {
11+
const tComponent = useTranslations('components.product')
12+
const label = tComponent('productHero.editManifest')
13+
14+
return (
15+
<a
16+
href={getManifestEditUrl(category, manifestId)}
17+
target="_blank"
18+
rel="noopener noreferrer"
19+
aria-label={label}
20+
title={label}
21+
className="inline-flex size-8 shrink-0 items-center justify-center text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-text)]"
22+
>
23+
<Github size={19} strokeWidth={1.6} aria-hidden="true" />
24+
</a>
25+
)
26+
}

src/components/product/ProductHero.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@ import { useTranslations } from 'next-intl'
22
import React from 'react'
33
import { DeprecatedBadge } from '@/components/controls/DeprecatedBadge'
44
import { VerifiedBadge } from '@/components/controls/VerifiedBadge'
5+
import { ManifestEditLink } from '@/components/product/ManifestEditLink'
56
import { Link } from '@/i18n/navigation'
67
import { renderLicense } from '@/lib/license'
8+
import type { ManifestCategory } from '@/lib/manifest-source'
79

810
export interface ProductHeroProps {
911
// Product identity
1012
name: string
1113
description: React.ReactNode
1214
vendor?: string
1315
vendorHref?: string
14-
category: 'CLI' | 'IDE' | 'DESKTOP' | 'EXTENSION' | 'PROVIDER' | 'MODEL' | 'VENDOR'
16+
category: ManifestCategory
17+
manifestId: string
1518
categoryLabel?: string // Optional custom label for the badge
1619
verified?: boolean // Whether the product is verified
1720
deprecated?: boolean // Whether the entity has been superseded or is no longer recommended
@@ -63,6 +66,7 @@ export function ProductHero({
6366
vendor,
6467
vendorHref,
6568
category,
69+
manifestId,
6670
categoryLabel,
6771
verified = false,
6872
deprecated = false,
@@ -103,6 +107,7 @@ export function ProductHero({
103107
<h1 className="text-5xl font-semibold tracking-[-0.04em] detail-page-h1">{name}</h1>
104108
{verified && <VerifiedBadge size="lg" />}
105109
{deprecated && <DeprecatedBadge size="lg" />}
110+
<ManifestEditLink category={category} manifestId={manifestId} />
106111
</div>
107112
<div className="absolute bottom-0 right-0 translate-x-[calc(100%+1rem)]">
108113
<div className="px-[var(--spacing-xs)] py-[2px] text-xs text-[var(--color-text-muted)] border-[1.5px] border-double border-[var(--color-border-strong)] whitespace-nowrap">

src/lib/manifest-source.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export type ManifestCategory =
2+
| 'CLI'
3+
| 'DESKTOP'
4+
| 'EXTENSION'
5+
| 'IDE'
6+
| 'MODEL'
7+
| 'PROVIDER'
8+
| 'VENDOR'
9+
10+
const manifestDirectories: Record<ManifestCategory, string> = {
11+
CLI: 'clis',
12+
DESKTOP: 'desktops',
13+
EXTENSION: 'extensions',
14+
IDE: 'ides',
15+
MODEL: 'models',
16+
PROVIDER: 'providers',
17+
VENDOR: 'vendors',
18+
}
19+
20+
export function getManifestEditUrl(category: ManifestCategory, manifestId: string): string {
21+
const directory = manifestDirectories[category]
22+
return `https://github.com/aicodingstack/aicodingstack.io/edit/main/manifests/${directory}/${encodeURIComponent(manifestId)}.json`
23+
}

0 commit comments

Comments
 (0)