@moontra/moonui-pro 2.18.1 → 2.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.18.1",
3
+ "version": "2.18.3",
4
4
  "description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -57,16 +57,29 @@ export function useGitHubData({
57
57
  const errorCountRef = useRef<number>(0) // Hata sayısını takip et
58
58
  const maxErrorCount = 2 // Maksimum hata sayısı
59
59
 
60
+ // checkMilestones fonksiyonunu ref olarak sakla - bu şekilde her render'da yeniden oluşturulmaz
61
+ const milestonesRef = useRef(milestones)
62
+ const onMilestoneReachedRef = useRef(onMilestoneReached)
63
+
64
+ // Ref'leri güncelle
65
+ useEffect(() => {
66
+ milestonesRef.current = milestones
67
+ }, [milestones])
68
+
69
+ useEffect(() => {
70
+ onMilestoneReachedRef.current = onMilestoneReached
71
+ }, [onMilestoneReached])
72
+
60
73
  const checkMilestones = useCallback((repos: GitHubRepository[]) => {
61
- if (!onMilestoneReached) return
74
+ if (!onMilestoneReachedRef.current) return
62
75
 
63
76
  repos.forEach(repo => {
64
77
  const previousStars = previousStarsRef.current.get(repo.full_name) || 0
65
78
  const currentStars = repo.stargazers_count
66
79
 
67
- milestones.forEach(milestone => {
80
+ milestonesRef.current.forEach(milestone => {
68
81
  if (previousStars < milestone && currentStars >= milestone) {
69
- onMilestoneReached({
82
+ onMilestoneReachedRef.current?.({
70
83
  count: milestone,
71
84
  reached: true,
72
85
  date: new Date().toISOString(),
@@ -77,7 +90,7 @@ export function useGitHubData({
77
90
 
78
91
  previousStarsRef.current.set(repo.full_name, currentStars)
79
92
  })
80
- }, [milestones, onMilestoneReached])
93
+ }, []) // Boş dependency array - fonksiyon asla yeniden oluşturulmaz
81
94
 
82
95
  const fetchData = useCallback(async () => {
83
96
  // Hata limiti aşıldıysa istek yapma
@@ -226,11 +239,11 @@ export function useGitHubData({
226
239
  }, [
227
240
  username,
228
241
  repository,
229
- repositories,
242
+ repositories?.join(','), // Array'i string'e çevir ki referans değişmesin
230
243
  token,
231
244
  sortBy,
232
245
  maxItems,
233
- checkMilestones,
246
+ checkMilestones, // Artık stable
234
247
  onDataUpdate,
235
248
  onError,
236
249
  ])
@@ -62,6 +62,27 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
62
62
  }) => {
63
63
  const { notify } = useGitHubNotifications(enableNotifications)
64
64
 
65
+ // onMilestoneReached callback'ini memoize et
66
+ const handleMilestoneReached = React.useCallback((milestone: any) => {
67
+ // Handle milestone reached
68
+ if (celebrateAt?.includes(milestone.count)) {
69
+ // Trigger celebration animation
70
+ confetti({
71
+ particleCount: 100,
72
+ spread: 70,
73
+ origin: { y: 0.6 },
74
+ })
75
+
76
+ // Show notification
77
+ notify(`🎉 Milestone Reached!`, {
78
+ body: `${milestone.count} stars achieved!`,
79
+ tag: `milestone-${milestone.count}`,
80
+ })
81
+ }
82
+
83
+ onMilestoneReached?.(milestone)
84
+ }, [celebrateAt, notify, onMilestoneReached])
85
+
65
86
  const {
66
87
  repos,
67
88
  stats,
@@ -81,29 +102,11 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
81
102
  maxItems,
82
103
  onError,
83
104
  onDataUpdate,
84
- onMilestoneReached: (milestone) => {
85
- // Handle milestone reached
86
- if (celebrateAt?.includes(milestone.count)) {
87
- // Trigger celebration animation
88
- confetti({
89
- particleCount: 100,
90
- spread: 70,
91
- origin: { y: 0.6 },
92
- })
93
-
94
- // Show notification
95
- notify(`🎉 Milestone Reached!`, {
96
- body: `${milestone.count} stars achieved!`,
97
- tag: `milestone-${milestone.count}`,
98
- })
99
- }
100
-
101
- onMilestoneReached?.(milestone)
102
- },
105
+ onMilestoneReached: handleMilestoneReached,
103
106
  milestones,
104
107
  })
105
108
 
106
- const handleExport = (format: "json" | "csv") => {
109
+ const handleExport = React.useCallback((format: "json" | "csv") => {
107
110
  if (!enableExport) return
108
111
 
109
112
  const timestamp = new Date().toISOString().split("T")[0]
@@ -116,7 +119,47 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
116
119
  } else {
117
120
  exportAsCSV(repos, `github-stars-${username}-${timestamp}.csv`)
118
121
  }
119
- }
122
+ }, [enableExport, repos, stats, username])
123
+
124
+ // handleDetailedExport fonksiyonunu memoize et
125
+ const handleDetailedExport = React.useCallback((e: React.MouseEvent) => {
126
+ if (!enableExport) return
127
+
128
+ // Show export dropdown
129
+ const dropdown = document.createElement("div")
130
+ dropdown.className = "absolute z-50 mt-2 w-48 rounded-md shadow-lg bg-popover border"
131
+ dropdown.innerHTML = `
132
+ <div class="py-1">
133
+ <button class="w-full text-left px-4 py-2 text-sm hover:bg-accent" data-format="json">
134
+ Export as JSON
135
+ </button>
136
+ <button class="w-full text-left px-4 py-2 text-sm hover:bg-accent" data-format="csv">
137
+ Export as CSV
138
+ </button>
139
+ </div>
140
+ `
141
+
142
+ dropdown.addEventListener("click", (e) => {
143
+ const target = e.target as HTMLElement
144
+ const format = target.getAttribute("data-format")
145
+ if (format === "json" || format === "csv") {
146
+ handleExport(format)
147
+ dropdown.remove()
148
+ }
149
+ })
150
+
151
+ document.body.appendChild(dropdown)
152
+
153
+ // Position dropdown
154
+ const rect = (e.target as HTMLElement).getBoundingClientRect()
155
+ dropdown.style.top = `${rect.bottom + window.scrollY}px`
156
+ dropdown.style.left = `${rect.left + window.scrollX}px`
157
+
158
+ // Remove on outside click
159
+ setTimeout(() => {
160
+ document.addEventListener("click", () => dropdown.remove(), { once: true })
161
+ }, 0)
162
+ }, [enableExport, handleExport])
120
163
 
121
164
  // Loading state
122
165
  if (loading) {
@@ -201,42 +244,7 @@ const GitHubStarsInternal: React.FC<GitHubStarsProps> = ({
201
244
  return (
202
245
  <DetailedVariant
203
246
  {...baseProps}
204
- onExport={() => {
205
- // Show export dropdown
206
- const dropdown = document.createElement("div")
207
- dropdown.className = "absolute z-50 mt-2 w-48 rounded-md shadow-lg bg-popover border"
208
- dropdown.innerHTML = `
209
- <div class="py-1">
210
- <button class="w-full text-left px-4 py-2 text-sm hover:bg-accent" data-format="json">
211
- Export as JSON
212
- </button>
213
- <button class="w-full text-left px-4 py-2 text-sm hover:bg-accent" data-format="csv">
214
- Export as CSV
215
- </button>
216
- </div>
217
- `
218
-
219
- dropdown.addEventListener("click", (e) => {
220
- const target = e.target as HTMLElement
221
- const format = target.getAttribute("data-format")
222
- if (format === "json" || format === "csv") {
223
- handleExport(format)
224
- dropdown.remove()
225
- }
226
- })
227
-
228
- document.body.appendChild(dropdown)
229
-
230
- // Position dropdown
231
- const rect = (e.target as HTMLElement).getBoundingClientRect()
232
- dropdown.style.top = `${rect.bottom + window.scrollY}px`
233
- dropdown.style.left = `${rect.left + window.scrollX}px`
234
-
235
- // Remove on outside click
236
- setTimeout(() => {
237
- document.addEventListener("click", () => dropdown.remove(), { once: true })
238
- }, 0)
239
- }}
247
+ onExport={handleDetailedExport}
240
248
  />
241
249
  )
242
250
  case "card":
@@ -38,18 +38,27 @@ export const MinimalVariant: React.FC<BaseVariantProps> = ({ repos, stats, class
38
38
 
39
39
  return (
40
40
  <div className={cn("flex items-center gap-4 text-sm", className)}>
41
- <div className="flex items-center gap-2">
41
+ <button
42
+ className="flex items-center gap-2 hover:text-primary transition-colors"
43
+ onClick={() => repos[0] && window.open(`https://github.com/${repos[0].owner?.login}`, '_blank')}
44
+ >
42
45
  <Github className="h-4 w-4" />
43
46
  <span className="font-medium">{repos.length} repositories</span>
44
- </div>
45
- <div className="flex items-center gap-2">
47
+ </button>
48
+ <button
49
+ className="flex items-center gap-2 hover:text-yellow-600 transition-colors"
50
+ onClick={() => repos[0] && window.open(`https://github.com/${repos[0].owner?.login}?tab=repositories&q=&type=&language=&sort=stargazers`, '_blank')}
51
+ >
46
52
  <Star className="h-4 w-4 text-yellow-500" />
47
53
  <span>{formatNumber(stats.totalStars)} stars</span>
48
- </div>
49
- <div className="flex items-center gap-2">
54
+ </button>
55
+ <button
56
+ className="flex items-center gap-2 hover:text-blue-600 transition-colors"
57
+ onClick={() => repos[0] && window.open(`https://github.com/${repos[0].owner?.login}?tab=repositories&q=&type=fork&language=&sort=`, '_blank')}
58
+ >
50
59
  <GitFork className="h-4 w-4 text-blue-500" />
51
60
  <span>{formatNumber(stats.totalForks)} forks</span>
52
- </div>
61
+ </button>
53
62
  </div>
54
63
  )
55
64
  }
@@ -75,14 +84,20 @@ export const CompactVariant: React.FC<BaseVariantProps> = ({
75
84
  <span className="font-semibold">{repos.length} Repos</span>
76
85
  </div>
77
86
  <div className="flex items-center gap-3 text-sm">
78
- <div className="flex items-center gap-1">
87
+ <button
88
+ className="flex items-center gap-1 hover:text-yellow-600 transition-colors cursor-pointer"
89
+ onClick={() => window.open(`https://github.com/${repos[0]?.owner?.login}/${repos[0]?.name}/stargazers`, '_blank')}
90
+ >
79
91
  <Star className="h-4 w-4 text-yellow-500" />
80
92
  <span>{formatNumber(stats.totalStars)}</span>
81
- </div>
82
- <div className="flex items-center gap-1">
93
+ </button>
94
+ <button
95
+ className="flex items-center gap-1 hover:text-blue-600 transition-colors cursor-pointer"
96
+ onClick={() => window.open(`https://github.com/${repos[0]?.owner?.login}/${repos[0]?.name}/forks`, '_blank')}
97
+ >
83
98
  <GitFork className="h-4 w-4 text-blue-500" />
84
99
  <span>{formatNumber(stats.totalForks)}</span>
85
- </div>
100
+ </button>
86
101
  </div>
87
102
  </div>
88
103
 
@@ -91,7 +106,7 @@ export const CompactVariant: React.FC<BaseVariantProps> = ({
91
106
  <div
92
107
  key={repo.id}
93
108
  className="flex items-center justify-between p-2 rounded-md hover:bg-accent cursor-pointer transition-colors"
94
- onClick={() => onRepositoryClick?.(repo)}
109
+ onClick={() => window.open(`https://github.com/${repo.owner?.login}/${repo.name}`, '_blank')}
95
110
  >
96
111
  <div className="flex-1 min-w-0">
97
112
  <p className="text-sm font-medium truncate">{repo.name}</p>
@@ -137,16 +152,19 @@ export const CardVariant: React.FC<BaseVariantProps> = ({
137
152
  transition={{ delay: index * 0.05 }}
138
153
  >
139
154
  <Card
140
- className="h-full hover:shadow-lg transition-shadow cursor-pointer"
141
- onClick={() => onRepositoryClick?.(repo)}
155
+ className="h-full hover:shadow-lg transition-shadow cursor-pointer group"
156
+ onClick={() => window.open(`https://github.com/${repo.owner?.login}/${repo.name}`, '_blank')}
142
157
  >
143
158
  <CardContent className="p-6">
144
159
  <div className="space-y-4">
145
- <div>
146
- <h3 className="font-semibold text-lg mb-1">{repo.name}</h3>
147
- <p className="text-sm text-muted-foreground line-clamp-2">
148
- {repo.description || "No description available"}
149
- </p>
160
+ <div className="flex items-start justify-between">
161
+ <div className="flex-1">
162
+ <h3 className="font-semibold text-lg mb-1">{repo.name}</h3>
163
+ <p className="text-sm text-muted-foreground line-clamp-2">
164
+ {repo.description || "No description available"}
165
+ </p>
166
+ </div>
167
+ <ExternalLink className="h-4 w-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
150
168
  </div>
151
169
 
152
170
  {repo.topics.length > 0 && (
@@ -172,14 +190,26 @@ export const CardVariant: React.FC<BaseVariantProps> = ({
172
190
  )}
173
191
  </div>
174
192
  <div className="flex items-center gap-3">
175
- <div className="flex items-center gap-1">
193
+ <button
194
+ className="flex items-center gap-1 hover:text-yellow-600 transition-colors"
195
+ onClick={(e) => {
196
+ e.stopPropagation()
197
+ window.open(`https://github.com/${repo.owner?.login}/${repo.name}/stargazers`, '_blank')
198
+ }}
199
+ >
176
200
  <Star className="h-4 w-4 text-yellow-500" />
177
201
  <span>{formatNumber(repo.stargazers_count)}</span>
178
- </div>
179
- <div className="flex items-center gap-1">
202
+ </button>
203
+ <button
204
+ className="flex items-center gap-1 hover:text-blue-600 transition-colors"
205
+ onClick={(e) => {
206
+ e.stopPropagation()
207
+ window.open(`https://github.com/${repo.owner?.login}/${repo.name}/forks`, '_blank')
208
+ }}
209
+ >
180
210
  <GitFork className="h-4 w-4 text-blue-500" />
181
211
  <span>{formatNumber(repo.forks_count)}</span>
182
- </div>
212
+ </button>
183
213
  </div>
184
214
  </div>
185
215
  </div>
@@ -203,30 +233,42 @@ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => voi
203
233
 
204
234
  return (
205
235
  <Card className={cn("w-full", className)}>
206
- <CardHeader>
236
+ <CardHeader className="pb-4">
207
237
  <div className="flex items-center justify-between">
208
238
  <CardTitle className="flex items-center gap-2">
209
239
  <Github className="h-6 w-6" />
210
240
  GitHub Repository Analytics
211
241
  </CardTitle>
212
- {onExport && (
213
- <Button onClick={onExport} variant="outline" size="sm">
214
- <Download className="h-4 w-4 mr-2" />
215
- Export
216
- </Button>
217
- )}
242
+ <div className="flex items-center gap-2">
243
+ {repos[0] && (
244
+ <Button
245
+ onClick={() => window.open(`https://github.com/${repos[0].owner?.login}`, '_blank')}
246
+ variant="outline"
247
+ size="sm"
248
+ >
249
+ <Github className="h-4 w-4 mr-2" />
250
+ View Profile
251
+ </Button>
252
+ )}
253
+ {onExport && (
254
+ <Button onClick={onExport} variant="outline" size="sm">
255
+ <Download className="h-4 w-4 mr-2" />
256
+ Export
257
+ </Button>
258
+ )}
259
+ </div>
218
260
  </div>
219
261
  </CardHeader>
220
- <CardContent>
221
- <Tabs defaultValue="overview" className="w-full">
222
- <TabsList className="grid w-full grid-cols-4">
262
+ <CardContent className="pt-0">
263
+ <Tabs defaultValue="overview" className="w-full mt-4">
264
+ <TabsList className="grid w-full grid-cols-4 mb-6">
223
265
  <TabsTrigger value="overview">Overview</TabsTrigger>
224
266
  <TabsTrigger value="repositories">Repositories</TabsTrigger>
225
267
  <TabsTrigger value="languages">Languages</TabsTrigger>
226
268
  <TabsTrigger value="activity">Activity</TabsTrigger>
227
269
  </TabsList>
228
270
 
229
- <TabsContent value="overview" className="space-y-4">
271
+ <TabsContent value="overview" className="space-y-6 mt-6">
230
272
  <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
231
273
  <StatCard
232
274
  icon={<Package className="h-4 w-4" />}
@@ -237,6 +279,7 @@ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => voi
237
279
  icon={<Star className="h-4 w-4 text-yellow-500" />}
238
280
  label="Total Stars"
239
281
  value={formatNumber(stats.totalStars)}
282
+ onClick={() => repos[0] && window.open(`https://github.com/${repos[0].owner?.login}?tab=repositories&q=&type=&language=&sort=stargazers`, '_blank')}
240
283
  />
241
284
  <StatCard
242
285
  icon={<GitFork className="h-4 w-4 text-blue-500" />}
@@ -251,36 +294,47 @@ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => voi
251
294
  </div>
252
295
 
253
296
  {stats.mostStarredRepo && (
254
- <Card>
255
- <CardContent className="p-4">
297
+ <Card
298
+ className="hover:shadow-md transition-shadow cursor-pointer"
299
+ onClick={() => window.open(`https://github.com/${stats.mostStarredRepo.owner?.login}/${stats.mostStarredRepo.name}`, '_blank')}
300
+ >
301
+ <CardContent className="p-6">
256
302
  <div className="flex items-center justify-between">
257
303
  <div>
258
- <p className="text-sm text-muted-foreground mb-1">Most Starred Repository</p>
259
- <h4 className="font-semibold">{stats.mostStarredRepo.name}</h4>
304
+ <p className="text-sm text-muted-foreground mb-2">Most Starred Repository</p>
305
+ <h4 className="font-semibold text-lg mb-1">{stats.mostStarredRepo.name}</h4>
260
306
  <p className="text-sm text-muted-foreground">
261
307
  {formatNumber(stats.mostStarredRepo.stargazers_count)} stars
262
308
  </p>
263
309
  </div>
264
- <TrendingUp className="h-8 w-8 text-muted-foreground" />
310
+ <div className="flex flex-col items-center gap-2">
311
+ <TrendingUp className="h-8 w-8 text-muted-foreground" />
312
+ <ExternalLink className="h-4 w-4 text-muted-foreground" />
313
+ </div>
265
314
  </div>
266
315
  </CardContent>
267
316
  </Card>
268
317
  )}
269
318
  </TabsContent>
270
319
 
271
- <TabsContent value="repositories" className="space-y-4">
272
- <div className="space-y-2">
320
+ <TabsContent value="repositories" className="space-y-4 mt-6">
321
+ <div className="space-y-3">
273
322
  {repos.map((repo) => (
274
323
  <Card
275
324
  key={repo.id}
276
- className="hover:shadow-md transition-shadow cursor-pointer"
277
- onClick={() => onRepositoryClick?.(repo)}
325
+ className="hover:shadow-md transition-shadow cursor-pointer group"
326
+ onClick={() => window.open(`https://github.com/${repo.owner?.login}/${repo.name}`, '_blank')}
278
327
  >
279
- <CardContent className="p-4">
328
+ <CardContent className="p-5">
280
329
  <div className="flex items-start justify-between">
281
330
  <div className="flex-1">
282
- <h4 className="font-semibold mb-1">{repo.name}</h4>
283
- <p className="text-sm text-muted-foreground mb-2">
331
+ <div className="flex items-center gap-2 mb-2">
332
+ <h4 className="font-semibold text-base">{repo.name}</h4>
333
+ {repo.private && (
334
+ <Badge variant="secondary" className="text-xs">Private</Badge>
335
+ )}
336
+ </div>
337
+ <p className="text-sm text-muted-foreground mb-3">
284
338
  {repo.description || "No description"}
285
339
  </p>
286
340
  <div className="flex items-center gap-4 text-sm">
@@ -295,21 +349,55 @@ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => voi
295
349
  <span>{repo.language}</span>
296
350
  </div>
297
351
  )}
298
- <div className="flex items-center gap-1">
352
+ <button
353
+ className="flex items-center gap-1 hover:text-yellow-600 transition-colors"
354
+ onClick={(e) => {
355
+ e.stopPropagation()
356
+ window.open(`https://github.com/${repo.owner?.login}/${repo.name}/stargazers`, '_blank')
357
+ }}
358
+ >
299
359
  <Star className="h-3 w-3" />
300
360
  <span>{formatNumber(repo.stargazers_count)}</span>
301
- </div>
302
- <div className="flex items-center gap-1">
361
+ </button>
362
+ <button
363
+ className="flex items-center gap-1 hover:text-blue-600 transition-colors"
364
+ onClick={(e) => {
365
+ e.stopPropagation()
366
+ window.open(`https://github.com/${repo.owner?.login}/${repo.name}/forks`, '_blank')
367
+ }}
368
+ >
303
369
  <GitFork className="h-3 w-3" />
304
370
  <span>{formatNumber(repo.forks_count)}</span>
305
- </div>
371
+ </button>
372
+ <button
373
+ className="flex items-center gap-1 hover:text-purple-600 transition-colors"
374
+ onClick={(e) => {
375
+ e.stopPropagation()
376
+ window.open(`https://github.com/${repo.owner?.login}/${repo.name}/issues`, '_blank')
377
+ }}
378
+ >
379
+ <Users className="h-3 w-3" />
380
+ <span>{repo.open_issues_count} issues</span>
381
+ </button>
306
382
  <div className="flex items-center gap-1">
307
383
  <Calendar className="h-3 w-3" />
308
384
  <span>{formatDate(repo.updated_at)}</span>
309
385
  </div>
310
386
  </div>
311
387
  </div>
312
- <ExternalLink className="h-4 w-4 text-muted-foreground" />
388
+ <div className="flex items-center gap-2">
389
+ <Button
390
+ size="sm"
391
+ variant="ghost"
392
+ className="opacity-0 group-hover:opacity-100 transition-opacity"
393
+ onClick={(e) => {
394
+ e.stopPropagation()
395
+ window.open(`https://github.com/${repo.owner?.login}/${repo.name}`, '_blank')
396
+ }}
397
+ >
398
+ <ExternalLink className="h-4 w-4" />
399
+ </Button>
400
+ </div>
313
401
  </div>
314
402
  </CardContent>
315
403
  </Card>
@@ -317,14 +405,14 @@ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => voi
317
405
  </div>
318
406
  </TabsContent>
319
407
 
320
- <TabsContent value="languages" className="space-y-4">
321
- <div className="space-y-3">
408
+ <TabsContent value="languages" className="space-y-6 mt-6">
409
+ <div className="space-y-4">
322
410
  {stats.languages.map((lang) => (
323
411
  <div key={lang.language} className="space-y-2">
324
412
  <div className="flex items-center justify-between text-sm">
325
413
  <div className="flex items-center gap-2">
326
414
  <div
327
- className="w-3 h-3 rounded-full"
415
+ className="w-3 h-3 rounded-full shadow-sm"
328
416
  style={{ backgroundColor: lang.color }}
329
417
  />
330
418
  <span className="font-medium">{lang.language}</span>
@@ -333,26 +421,62 @@ export const DetailedVariant: React.FC<BaseVariantProps & { onExport?: () => voi
333
421
  {lang.count} repos ({lang.percentage.toFixed(1)}%)
334
422
  </span>
335
423
  </div>
336
- <Progress value={lang.percentage} className="h-2" />
424
+ <div className="px-1">
425
+ <Progress value={lang.percentage} className="h-2" />
426
+ </div>
337
427
  </div>
338
428
  ))}
339
429
  </div>
430
+
431
+ {repos[0] && (
432
+ <div className="pt-4 border-t">
433
+ <Button
434
+ variant="outline"
435
+ className="w-full"
436
+ onClick={() => window.open(`https://github.com/${repos[0].owner?.login}?tab=repositories&q=&type=&language=&sort=`, '_blank')}
437
+ >
438
+ <BarChart3 className="h-4 w-4 mr-2" />
439
+ View Language Statistics on GitHub
440
+ </Button>
441
+ </div>
442
+ )}
340
443
  </TabsContent>
341
444
 
342
- <TabsContent value="activity" className="space-y-4">
343
- <div className="space-y-2">
344
- {stats.recentActivity.map((activity, index) => (
345
- <div key={index} className="flex items-center gap-3 p-3 rounded-lg bg-accent/50">
346
- <Activity className="h-4 w-4 text-muted-foreground" />
347
- <div className="flex-1">
348
- <p className="text-sm">{activity.description}</p>
349
- <p className="text-xs text-muted-foreground">
350
- {activity.repository} {formatDate(activity.timestamp)}
351
- </p>
445
+ <TabsContent value="activity" className="space-y-4 mt-6">
446
+ <div className="space-y-3">
447
+ {stats.recentActivity.map((activity, index) => {
448
+ const repo = repos.find(r => r.name === activity.repository)
449
+ return (
450
+ <div
451
+ key={index}
452
+ className="flex items-center gap-4 p-4 rounded-lg bg-accent/30 hover:bg-accent/50 transition-colors cursor-pointer"
453
+ onClick={() => repo && window.open(`https://github.com/${repo.owner?.login}/${repo.name}`, '_blank')}
454
+ >
455
+ <Activity className="h-4 w-4 text-muted-foreground flex-shrink-0" />
456
+ <div className="flex-1 min-w-0">
457
+ <p className="text-sm font-medium">{activity.description}</p>
458
+ <p className="text-xs text-muted-foreground mt-1">
459
+ {activity.repository} • {formatDate(activity.timestamp)}
460
+ </p>
461
+ </div>
462
+ <ExternalLink className="h-3 w-3 text-muted-foreground flex-shrink-0" />
352
463
  </div>
353
- </div>
354
- ))}
464
+ )
465
+ })}
355
466
  </div>
467
+
468
+ {repos[0] && (
469
+ <div className="pt-4 border-t">
470
+ <Button
471
+ variant="outline"
472
+ className="w-full"
473
+ onClick={() => window.open(`https://github.com/${repos[0].owner?.login}?tab=repositories&q=&type=&language=&sort=updated`, '_blank')}
474
+ >
475
+ <Activity className="h-4 w-4 mr-2" />
476
+ View All Activity on GitHub
477
+ </Button>
478
+ </div>
479
+ )}
356
480
  </TabsContent>
357
481
  </Tabs>
358
482
  </CardContent>
@@ -365,15 +489,24 @@ const StatCard: React.FC<{
365
489
  icon: React.ReactNode
366
490
  label: string
367
491
  value: string | number
368
- }> = ({ icon, label, value }) => (
369
- <Card>
370
- <CardContent className="p-4">
492
+ onClick?: () => void
493
+ }> = ({ icon, label, value, onClick }) => (
494
+ <Card
495
+ className={cn(
496
+ "transition-all",
497
+ onClick && "hover:shadow-md cursor-pointer hover:scale-105"
498
+ )}
499
+ onClick={onClick}
500
+ >
501
+ <CardContent className="p-5">
371
502
  <div className="flex items-center justify-between">
372
503
  <div>
373
- <p className="text-sm text-muted-foreground">{label}</p>
504
+ <p className="text-sm text-muted-foreground mb-1">{label}</p>
374
505
  <p className="text-2xl font-bold">{value}</p>
375
506
  </div>
376
- {icon}
507
+ <div className="text-muted-foreground">
508
+ {icon}
509
+ </div>
377
510
  </div>
378
511
  </CardContent>
379
512
  </Card>