Skip to content

Commit 331ab7d

Browse files
ericyangpanclaude
andcommitted
feat(ui): enhance AI coding landscape with interactive visualizations
Add comprehensive landscape visualization features including: - Vendor-product matrix view - Interactive relationship graph - Product categories with filtering - Statistics dashboard - Multi-view tabs for different perspectives - OG image for social sharing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 6ac2892 commit 331ab7d

File tree

9 files changed

+2165
-102
lines changed

9 files changed

+2165
-102
lines changed

public/repository-open-graph.png

93.9 KB
Loading
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'use client';
2+
3+
import LandscapeViewTabs from './LandscapeViewTabs';
4+
import { BackToNavigation } from '@/components/controls/BackToNavigation';
5+
import type {
6+
VendorMatrixRow,
7+
LandscapeStats,
8+
RelationshipNode,
9+
RelationshipEdge,
10+
LandscapeProduct,
11+
} from '@/lib/landscape-data';
12+
13+
interface LandscapePageProps {
14+
matrixData: VendorMatrixRow[];
15+
graphData: {
16+
nodes: RelationshipNode[];
17+
edges: RelationshipEdge[];
18+
};
19+
productsByCategory: {
20+
ides: LandscapeProduct[];
21+
clis: LandscapeProduct[];
22+
extensions: LandscapeProduct[];
23+
models: LandscapeProduct[];
24+
providers: LandscapeProduct[];
25+
};
26+
stats: LandscapeStats;
27+
locale: string;
28+
translations: {
29+
title: string;
30+
description: string;
31+
backTitle: string;
32+
};
33+
}
34+
35+
export default function LandscapePage({
36+
matrixData,
37+
graphData,
38+
productsByCategory,
39+
stats,
40+
locale,
41+
translations,
42+
}: LandscapePageProps) {
43+
return (
44+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)] py-[var(--spacing-xl)]">
45+
{/* Page Header */}
46+
<div className="text-center mb-[var(--spacing-xl)]">
47+
<h1 className="text-[3rem] font-semibold tracking-[-0.04em] mb-[var(--spacing-sm)]">
48+
<span className="text-[var(--color-text-muted)] font-light mr-[var(--spacing-xs)]">🗺️</span>
49+
{translations.title}
50+
</h1>
51+
<p className="text-lg text-[var(--color-text-secondary)] font-light max-w-[800px] mx-auto">
52+
{translations.description}
53+
</p>
54+
</div>
55+
56+
{/* Quick Stats Summary */}
57+
<div className="mb-[var(--spacing-xl)] p-[var(--spacing-lg)] border border-[var(--color-border)] bg-gradient-to-br from-blue-500/5 to-purple-500/5">
58+
<div className="grid grid-cols-2 md:grid-cols-5 gap-[var(--spacing-md)] text-center">
59+
<div>
60+
<div className="text-2xl font-bold tracking-tight">{stats.totalProducts}</div>
61+
<div className="text-xs text-[var(--color-text-muted)] mt-1">Products</div>
62+
</div>
63+
<div>
64+
<div className="text-2xl font-bold tracking-tight">{stats.totalVendors}</div>
65+
<div className="text-xs text-[var(--color-text-muted)] mt-1">Vendors</div>
66+
</div>
67+
<div>
68+
<div className="text-2xl font-bold tracking-tight">{stats.counts.ides}</div>
69+
<div className="text-xs text-[var(--color-text-muted)] mt-1">IDEs</div>
70+
</div>
71+
<div>
72+
<div className="text-2xl font-bold tracking-tight">{stats.counts.clis}</div>
73+
<div className="text-xs text-[var(--color-text-muted)] mt-1">CLIs</div>
74+
</div>
75+
<div>
76+
<div className="text-2xl font-bold tracking-tight">{stats.counts.extensions}</div>
77+
<div className="text-xs text-[var(--color-text-muted)] mt-1">Extensions</div>
78+
</div>
79+
</div>
80+
</div>
81+
82+
{/* Tabs */}
83+
<LandscapeViewTabs
84+
matrixData={matrixData}
85+
graphData={graphData}
86+
productsByCategory={productsByCategory}
87+
stats={stats}
88+
locale={locale}
89+
/>
90+
91+
{/* Back to Overview */}
92+
<div className="mt-[var(--spacing-xl)]">
93+
<BackToNavigation href="/ai-coding-stack" title={translations.backTitle} />
94+
</div>
95+
</div>
96+
);
97+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import dynamic from 'next/dynamic';
5+
import VendorMatrix from './VendorMatrix';
6+
import ProductCategories from './ProductCategories';
7+
import StatisticsDashboard from './StatisticsDashboard';
8+
import type {
9+
VendorMatrixRow,
10+
LandscapeStats,
11+
RelationshipNode,
12+
RelationshipEdge,
13+
LandscapeProduct,
14+
} from '@/lib/landscape-data';
15+
16+
// Dynamically import RelationshipGraph to avoid SSR issues with React Flow
17+
const RelationshipGraph = dynamic(() => import('./RelationshipGraph').then((mod) => mod.default), {
18+
ssr: false,
19+
loading: () => (
20+
<div className="h-[700px] border border-[var(--color-border)] bg-[var(--color-bg-subtle)] rounded-lg flex items-center justify-center">
21+
<div className="text-[var(--color-text-muted)]">Loading relationship graph...</div>
22+
</div>
23+
),
24+
});
25+
26+
interface LandscapeViewTabsProps {
27+
matrixData: VendorMatrixRow[];
28+
graphData: {
29+
nodes: RelationshipNode[];
30+
edges: RelationshipEdge[];
31+
};
32+
productsByCategory: {
33+
ides: LandscapeProduct[];
34+
clis: LandscapeProduct[];
35+
extensions: LandscapeProduct[];
36+
models: LandscapeProduct[];
37+
providers: LandscapeProduct[];
38+
};
39+
stats: LandscapeStats;
40+
locale: string;
41+
}
42+
43+
type TabKey = 'matrix' | 'graph' | 'categories' | 'stats';
44+
45+
interface Tab {
46+
key: TabKey;
47+
label: string;
48+
icon: string;
49+
description: string;
50+
}
51+
52+
const TABS: Tab[] = [
53+
{
54+
key: 'matrix',
55+
label: 'Vendor Matrix',
56+
icon: '📊',
57+
description: 'Vendor ecosystem matrix view',
58+
},
59+
{
60+
key: 'graph',
61+
label: 'Relationship Graph',
62+
icon: '🕸️',
63+
description: 'Interactive network visualization',
64+
},
65+
{
66+
key: 'categories',
67+
label: 'Product Categories',
68+
icon: '📂',
69+
description: 'Browse products by category',
70+
},
71+
{
72+
key: 'stats',
73+
label: 'Statistics',
74+
icon: '📈',
75+
description: 'Ecosystem statistics and insights',
76+
},
77+
];
78+
79+
export default function LandscapeViewTabs({
80+
matrixData,
81+
graphData,
82+
productsByCategory,
83+
stats,
84+
locale,
85+
}: LandscapeViewTabsProps) {
86+
const [activeTab, setActiveTab] = useState<TabKey>('matrix');
87+
88+
return (
89+
<div className="space-y-[var(--spacing-lg)]">
90+
{/* Tab Navigation */}
91+
<div className="border-b border-[var(--color-border)]">
92+
<div className="flex flex-wrap gap-2 pb-2">
93+
{TABS.map((tab) => (
94+
<button
95+
key={tab.key}
96+
onClick={() => setActiveTab(tab.key)}
97+
className={`px-4 py-2 border transition-all flex items-center gap-2 ${
98+
activeTab === tab.key
99+
? 'border-[var(--color-border-strong)] bg-[var(--color-bg-subtle)] -mb-[1px] border-b-transparent font-semibold'
100+
: 'border-transparent hover:border-[var(--color-border)]'
101+
}`}
102+
>
103+
<span>{tab.icon}</span>
104+
<div className="text-left">
105+
<div className="text-sm">{tab.label}</div>
106+
<div className="text-xs text-[var(--color-text-muted)]">{tab.description}</div>
107+
</div>
108+
</button>
109+
))}
110+
</div>
111+
</div>
112+
113+
{/* Tab Content */}
114+
<div className="min-h-[600px]">
115+
{activeTab === 'matrix' && <VendorMatrix matrixData={matrixData} locale={locale} />}
116+
117+
{activeTab === 'graph' && <RelationshipGraph graphData={graphData} />}
118+
119+
{activeTab === 'categories' && <ProductCategories productsByCategory={productsByCategory} />}
120+
121+
{activeTab === 'stats' && <StatisticsDashboard stats={stats} />}
122+
</div>
123+
</div>
124+
);
125+
}

0 commit comments

Comments
 (0)