cfahlgren1 HF Staff commited on
Commit
9aaca36
·
verified ·
1 Parent(s): 1d143c4

Update src/pages/index.tsx

Browse files
Files changed (1) hide show
  1. src/pages/index.tsx +57 -217
src/pages/index.tsx CHANGED
@@ -12,19 +12,19 @@ import UserSearchDialog from "../components/UserSearchDialog";
12
 
13
  const PROVIDERS: ProviderInfo[] = [
14
  { color: "#ff7000", authors: ["mistralai"] },
15
- { color: "#1877F2", authors: ["meta-llama", "facebook"] },
16
  { color: "#10A37F", authors: ["openai"] },
17
  { color: "#cc785c", authors: ["Anthropic"] },
18
  { color: "#DB4437", authors: ["google"] },
19
  { color: "#5E35B1", authors: ["allenai"] },
20
- { color: "#0066CC", authors: ["apple"] },
21
  { color: "#FEB800", authors: ["microsoft"] },
22
  { color: "#76B900", authors: ["nvidia"] },
23
- { color: "#00A8E0", authors: ["deepseek-ai"] },
24
- { color: "#6366F1", authors: ["Qwen"] },
25
- { color: "#FF6B6B", authors: ["CohereLabs"] },
26
- { color: "#4ECDC4", authors: ["ibm-granite"] },
27
- { color: "#A855F7", authors: ["stabilityai"] },
28
  ];
29
 
30
  export async function getStaticProps() {
@@ -42,7 +42,7 @@ export async function getStaticProps() {
42
  calendarData,
43
  providers: updatedProviders,
44
  },
45
- revalidate: 3600,
46
  };
47
  } catch (error) {
48
  console.error("Error fetching data:", error);
@@ -51,74 +51,22 @@ export async function getStaticProps() {
51
  calendarData: {},
52
  providers: PROVIDERS,
53
  },
54
- revalidate: 60,
55
  };
56
  }
57
  }
58
 
59
- const ProviderCard = React.memo(({
60
- provider,
61
- calendarData,
62
- isExpanded,
63
- onToggle
64
- }: {
65
- provider: ProviderInfo,
66
- calendarData: CalendarData,
67
- isExpanded: boolean,
68
- onToggle: () => void
69
- }) => {
70
  const providerName = provider.fullName || provider.authors[0];
71
- const totalActivity = calendarData[providerName]?.reduce((sum, day) => sum + day.count, 0) || 0;
72
- const recentActivity = calendarData[providerName]?.slice(-30).reduce((sum, day) => sum + day.count, 0) || 0;
73
-
74
  return (
75
- <div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden transition-all duration-200 hover:shadow-md">
76
- <button
77
- onClick={onToggle}
78
- className="w-full px-6 py-4 flex items-center justify-between hover:bg-gray-50 transition-colors"
79
- >
80
- <div className="flex items-center gap-4">
81
- {provider.avatarUrl && (
82
- <img
83
- src={provider.avatarUrl}
84
- alt={providerName}
85
- className="w-12 h-12 rounded-lg object-cover"
86
- />
87
- )}
88
- <div className="text-left">
89
- <h3 className="font-semibold text-lg text-gray-900">{providerName}</h3>
90
- <div className="flex gap-4 text-sm text-gray-600">
91
- <span>Total: <span className="font-medium text-gray-900">{totalActivity.toLocaleString()}</span></span>
92
- <span>Last 30 days: <span className="font-medium text-gray-900">{recentActivity.toLocaleString()}</span></span>
93
- </div>
94
- </div>
95
- </div>
96
- <div className="flex items-center gap-3">
97
- <div className="w-3 h-3 rounded-full" style={{ backgroundColor: provider.color }}></div>
98
- <svg
99
- className={`w-5 h-5 text-gray-400 transition-transform ${isExpanded ? 'rotate-180' : ''}`}
100
- fill="none"
101
- stroke="currentColor"
102
- viewBox="0 0 24 24"
103
- >
104
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
105
- </svg>
106
- </div>
107
- </button>
108
-
109
- {isExpanded && (
110
- <div className="px-6 pb-6 border-t border-gray-100">
111
- <div className="pt-4">
112
- <Heatmap
113
- data={calendarData[providerName]}
114
- color={provider.color}
115
- providerName={providerName}
116
- fullName={provider.fullName ?? providerName}
117
- avatarUrl={provider.avatarUrl ?? ''}
118
- />
119
- </div>
120
- </div>
121
- )}
122
  </div>
123
  );
124
  });
@@ -128,9 +76,6 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
128
  providers,
129
  }) => {
130
  const [isLoading, setIsLoading] = useState(true);
131
- const [expandedProviders, setExpandedProviders] = useState<Set<string>>(new Set());
132
- const [filterQuery, setFilterQuery] = useState("");
133
- const [sortBy, setSortBy] = useState<"total" | "recent">("total");
134
 
135
  useEffect(() => {
136
  if (calendarData && Object.keys(calendarData).length > 0) {
@@ -138,154 +83,49 @@ const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
138
  }
139
  }, [calendarData]);
140
 
141
- const sortedAndFilteredProviders = useMemo(() => {
142
- let filtered = providers;
143
-
144
- if (filterQuery) {
145
- filtered = providers.filter(p =>
146
- (p.fullName || p.authors[0]).toLowerCase().includes(filterQuery.toLowerCase())
147
- );
148
- }
149
-
150
- return filtered.sort((a, b) => {
151
- const aName = a.fullName || a.authors[0];
152
- const bName = b.fullName || b.authors[0];
153
-
154
- if (sortBy === "total") {
155
- return calendarData[bName]?.reduce((sum, day) => sum + day.count, 0) || 0 -
156
- calendarData[aName]?.reduce((sum, day) => sum + day.count, 0) || 0;
157
- } else {
158
- return calendarData[bName]?.slice(-30).reduce((sum, day) => sum + day.count, 0) || 0 -
159
- calendarData[aName]?.slice(-30).reduce((sum, day) => sum + day.count, 0) || 0;
160
- }
161
- });
162
- }, [providers, calendarData, filterQuery, sortBy]);
163
-
164
- const toggleProvider = (providerName: string) => {
165
- setExpandedProviders(prev => {
166
- const newSet = new Set(prev);
167
- if (newSet.has(providerName)) {
168
- newSet.delete(providerName);
169
- } else {
170
- newSet.add(providerName);
171
- }
172
- return newSet;
173
- });
174
- };
175
-
176
- const toggleAll = () => {
177
- if (expandedProviders.size === sortedAndFilteredProviders.length) {
178
- setExpandedProviders(new Set());
179
- } else {
180
- setExpandedProviders(new Set(sortedAndFilteredProviders.map(p => p.fullName || p.authors[0])));
181
- }
182
- };
183
 
184
  return (
185
- <div className="min-h-screen bg-gray-50">
186
- <div className="w-full max-w-7xl mx-auto px-4 py-12">
187
- {/* Header */}
188
- <div className="text-center mb-12">
189
- <h1 className="text-5xl font-bold text-gray-900 mb-4">
190
- AI Labs Activity Dashboard
191
- </h1>
192
- <p className="text-lg text-gray-600 mb-8">
193
- Track models, datasets, and spaces from leading AI organizations on Hugging Face
194
- </p>
195
-
196
- {/* Controls */}
197
- <div className="flex flex-col sm:flex-row gap-4 justify-center items-center max-w-2xl mx-auto">
198
- <div className="relative flex-1 w-full">
199
- <input
200
- type="text"
201
- placeholder="Search organizations..."
202
- value={filterQuery}
203
- onChange={(e) => setFilterQuery(e.target.value)}
204
- className="w-full px-4 py-2 pl-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
205
- />
206
- <svg className="absolute left-3 top-2.5 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
207
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
208
- </svg>
209
- </div>
210
-
211
- <div className="flex gap-2">
212
- <select
213
- value={sortBy}
214
- onChange={(e) => setSortBy(e.target.value as "total" | "recent")}
215
- className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
216
- >
217
- <option value="total">Sort by Total</option>
218
- <option value="recent">Sort by Recent</option>
219
- </select>
220
-
221
- <button
222
- onClick={toggleAll}
223
- className="px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
224
- >
225
- {expandedProviders.size === sortedAndFilteredProviders.length ? 'Collapse All' : 'Expand All'}
226
- </button>
227
-
228
- <UserSearchDialog />
229
- </div>
230
- </div>
231
  </div>
232
-
233
- {/* Stats Summary */}
234
- {!isLoading && (
235
- <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-8">
236
- <div className="bg-white rounded-lg p-6 text-center shadow-sm border border-gray-200">
237
- <div className="text-3xl font-bold text-gray-900">
238
- {providers.length}
239
- </div>
240
- <div className="text-sm text-gray-600">Organizations</div>
241
- </div>
242
- <div className="bg-white rounded-lg p-6 text-center shadow-sm border border-gray-200">
243
- <div className="text-3xl font-bold text-gray-900">
244
- {Object.values(calendarData).reduce((total, days) =>
245
- total + days.reduce((sum, day) => sum + day.count, 0), 0
246
- ).toLocaleString()}
247
- </div>
248
- <div className="text-sm text-gray-600">Total Activities</div>
249
- </div>
250
- <div className="bg-white rounded-lg p-6 text-center shadow-sm border border-gray-200">
251
- <div className="text-3xl font-bold text-gray-900">
252
- {Object.values(calendarData).reduce((total, days) =>
253
- total + days.slice(-30).reduce((sum, day) => sum + day.count, 0), 0
254
- ).toLocaleString()}
255
- </div>
256
- <div className="text-sm text-gray-600">Last 30 Days</div>
257
- </div>
258
- </div>
259
- )}
260
-
261
- {/* Provider List */}
262
- {isLoading ? (
263
- <div className="flex justify-center items-center h-64">
264
- <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900"></div>
265
- </div>
266
- ) : (
267
- <div className="space-y-4">
268
- {sortedAndFilteredProviders.length === 0 ? (
269
- <div className="text-center py-12 text-gray-500">
270
- No organizations found matching "{filterQuery}"
271
- </div>
272
- ) : (
273
- sortedAndFilteredProviders.map((provider) => {
274
- const providerName = provider.fullName || provider.authors[0];
275
- return (
276
- <ProviderCard
277
- key={providerName}
278
- provider={provider}
279
- calendarData={calendarData}
280
- isExpanded={expandedProviders.has(providerName)}
281
- onToggle={() => toggleProvider(providerName)}
282
- />
283
- );
284
- })
285
- )}
286
- </div>
287
- )}
288
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  </div>
290
  );
291
  };
 
12
 
13
  const PROVIDERS: ProviderInfo[] = [
14
  { color: "#ff7000", authors: ["mistralai"] },
15
+ { color: "#1877F2", authors: ["meta-llama", "facebook", ] },
16
  { color: "#10A37F", authors: ["openai"] },
17
  { color: "#cc785c", authors: ["Anthropic"] },
18
  { color: "#DB4437", authors: ["google"] },
19
  { color: "#5E35B1", authors: ["allenai"] },
20
+ { color: "#0088cc", authors: ["apple"] },
21
  { color: "#FEB800", authors: ["microsoft"] },
22
  { color: "#76B900", authors: ["nvidia"] },
23
+ { color: "#0088cc", authors: ["deepseek-ai"] },
24
+ { color: "#0088cc", authors: ["Qwen"] },
25
+ { color: "#4C6EE6", authors: ["CohereLabs"] },
26
+ { color: "#4C6EE6", authors: ["ibm-granite"] },
27
+ { color: "#A020F0", authors: ["stabilityai"] },
28
  ];
29
 
30
  export async function getStaticProps() {
 
42
  calendarData,
43
  providers: updatedProviders,
44
  },
45
+ revalidate: 3600, // regenerate every hour
46
  };
47
  } catch (error) {
48
  console.error("Error fetching data:", error);
 
51
  calendarData: {},
52
  providers: PROVIDERS,
53
  },
54
+ revalidate: 60, // retry after 1 minute if there was an error
55
  };
56
  }
57
  }
58
 
59
+ const ProviderHeatmap = React.memo(({ provider, calendarData }: { provider: ProviderInfo, calendarData: CalendarData }) => {
 
 
 
 
 
 
 
 
 
 
60
  const providerName = provider.fullName || provider.authors[0];
 
 
 
61
  return (
62
+ <div key={providerName} className="flex flex-col items-center">
63
+ <Heatmap
64
+ data={calendarData[providerName]}
65
+ color={provider.color}
66
+ providerName={providerName}
67
+ fullName={provider.fullName ?? providerName}
68
+ avatarUrl={provider.avatarUrl ?? ''}
69
+ />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  </div>
71
  );
72
  });
 
76
  providers,
77
  }) => {
78
  const [isLoading, setIsLoading] = useState(true);
 
 
 
79
 
80
  useEffect(() => {
81
  if (calendarData && Object.keys(calendarData).length > 0) {
 
83
  }
84
  }, [calendarData]);
85
 
86
+ const sortedProviders = useMemo(() =>
87
+ providers.sort((a, b) =>
88
+ calendarData[b.fullName || b.authors[0]].reduce(
89
+ (sum, day) => sum + day.count,
90
+ 0
91
+ ) -
92
+ calendarData[a.fullName || a.authors[0]].reduce(
93
+ (sum, day) => sum + day.count,
94
+ 0
95
+ )
96
+ ),
97
+ [providers, calendarData]
98
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  return (
101
+ <div className="w-full max-w-screen-lg mx-auto p-4 py-16">
102
+ <h1 className="text-3xl lg:text-5xl mt-16 font-bold text-center mb-2">
103
+ Hugging Face Heatmap 🤗
104
+ </h1>
105
+ <div className="text-center text-sm my-8 space-y-4">
106
+ <p>
107
+ Models, Datasets, and Spaces from the top AI labs.
108
+ </p>
109
+ <div className="flex justify-center space-x-4">
110
+ <UserSearchDialog />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  </div>
113
+
114
+ <div className="h-px max-w-lg mx-auto bg-gray-200 my-16" />
115
+
116
+ {isLoading ? (
117
+ <p className="text-center">Loading...</p>
118
+ ) : (
119
+ <div className="space-y-16">
120
+ {sortedProviders.map((provider) => (
121
+ <ProviderHeatmap
122
+ key={provider.fullName || provider.authors[0]}
123
+ provider={provider}
124
+ calendarData={calendarData}
125
+ />
126
+ ))}
127
+ </div>
128
+ )}
129
  </div>
130
  );
131
  };