✨ feat: update index.astro page

swadhinbiswas committed 3 months ago
d60ea80
1 parent 5f99cf3
commit d60ea80
1 file changed
+158 -53
Showing 1 changed file+158-53
▼src/pages/[owner]/index.astro
+158-53
3
3
import BaseLayout from "@/layouts/BaseLayout.astro";
4
4
import { and, desc, eq, sql } from "drizzle-orm";
5
5
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
6
+import { resolveRepoPath } from "@/lib/git-storage";
7
+import { getFileContent } from "@/lib/git";
6
8
7
9
const { owner: username } = Astro.params;
8
10
const db = getDatabase() as NodePgDatabase<typeof schema>;
79
81
});
80
82
} catch {}
81
83
84
+// 8. Fetch profile README from {username}/{username} repo
85
+let profileReadme = "";
86
+try {
87
+ const profileRepo = await db.query.repositories.findFirst({
88
+ where: and(
89
+ eq(schema.repositories.ownerId, user.id),
90
+ eq(schema.repositories.name, username!),
91
+ ),
92
+ });
93
+ if (profileRepo) {
94
+ const localPath = await resolveRepoPath(profileRepo.diskPath);
95
+ const fileData = await getFileContent(localPath, "README.md", profileRepo.defaultBranch || "main");
96
+ profileReadme = fileData?.content || "";
97
+ }
98
+} catch {}
99
+
82
100
// Language colors (GitHub style)
83
101
const langColors: Record<string, string> = {
84
102
JavaScript: "bg-[#f1e05a]",
331
349
332
350
{tab === "overview" && (
333
351
<div class="space-y-6">
352
+ <!-- Profile README -->
353
+ {profileReadme && (
354
+ <div class="border border-border bg-card rounded-lg p-6 markdown-body">
355
+ <div set:html={profileReadme} />
356
+ </div>
357
+ )}
358
+
334
359
<!-- Pinned Repositories -->
335
360
<div>
336
361
<div class="flex items-center justify-between mb-3">
337
- <span class="text-sm font-semibold text-foreground">Popular repositories</span>
362
+ <span class="text-sm font-semibold text-foreground">Pinned</span>
338
363
{isOwner && (
339
364
<a href={`/${user.username}?tab=repositories`} class="text-xs text-muted-foreground hover:text-blue-500 transition-colors">Customize your pins</a>
340
365
)}
384
409
385
410
<!-- Contribution Graph -->
386
411
<div>
387
- <div class="text-sm text-foreground mb-2">
388
- <span class="font-semibold">{contributionCount}</span> contributions in the last year
412
+ <div class="flex items-center justify-between mb-3">
413
+ <div class="text-sm text-foreground">
414
+ <span class="font-semibold">{contributionCount}</span> contributions in the last year
415
+ </div>
389
416
</div>
390
- <div class="border border-border/60 bg-background rounded-md p-4">
417
+ <div class="border border-border bg-card rounded-lg p-4">
391
418
<div class="w-full overflow-x-auto no-scrollbar">
392
- <div class="inline-flex gap-[3px] justify-start min-w-full">
393
- {weeks.map(week => (
419
+ <div class="inline-flex gap-[3px] justify-start">
420
+ {weeks.map((week, wi) => (
394
421
<div class="flex flex-col gap-[3px]">
395
- {week.map(dayLevel => (
396
- <div
397
- class:list={[
398
- "w-[10px] h-[10px] rounded-[2px] transition-colors",
399
- dayLevel === 0 && "bg-[#ebedf0] dark:bg-[#161b22]",
400
- dayLevel === 1 && "bg-[#9be9a8] dark:bg-[#0e4429]",
401
- dayLevel === 2 && "bg-[#40c463] dark:bg-[#006d32]",
402
- dayLevel === 3 && "bg-[#30a14e] dark:bg-[#26a641]",
403
- dayLevel === 4 && "bg-[#216e39] dark:bg-[#39d353]",
404
- ]}
405
- />
406
- ))}
422
+ {week.map((dayLevel, di) => {
423
+ const totalDays = wi * 7 + di;
424
+ const date = new Date();
425
+ date.setDate(date.getDate() - (364 - totalDays));
426
+ const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
427
+ return (
428
+ <div
429
+ class:list={[
430
+ "w-[11px] h-[11px] rounded-[2px] transition-all hover:ring-1 hover:ring-foreground/20",
431
+ dayLevel === 0 && "bg-muted",
432
+ dayLevel === 1 && "bg-primary/20",
433
+ dayLevel === 2 && "bg-primary/40",
434
+ dayLevel === 3 && "bg-primary/60",
435
+ dayLevel === 4 && "bg-primary",
436
+ ]}
437
+ title={`${dayLevel} contributions on ${dateStr}`}
438
+ />
439
+ );
440
+ })}
407
441
</div>
408
442
))}
409
443
</div>
410
444
</div>
411
- <div class="flex items-center justify-between mt-3 text-[11px] text-muted-foreground">
412
- <span>Learn how we count contributions</span>
413
- <div class="flex items-center gap-[3px]">
445
+ <div class="flex items-center justify-between mt-4 pt-3 border-t border-border text-xs text-muted-foreground">
446
+ <a href="#" class="hover:text-foreground transition-colors underline decoration-dotted underline-offset-2">Learn how we count contributions</a>
447
+ <div class="flex items-center gap-1.5">
414
448
<span>Less</span>
415
- <div class="w-[10px] h-[10px] rounded-[2px] bg-[#ebedf0] dark:bg-[#161b22]" />
416
- <div class="w-[10px] h-[10px] rounded-[2px] bg-[#9be9a8] dark:bg-[#0e4429]" />
417
- <div class="w-[10px] h-[10px] rounded-[2px] bg-[#40c463] dark:bg-[#006d32]" />
418
- <div class="w-[10px] h-[10px] rounded-[2px] bg-[#30a14e] dark:bg-[#26a641]" />
419
- <div class="w-[10px] h-[10px] rounded-[2px] bg-[#216e39] dark:bg-[#39d353]" />
449
+ <div class="w-[11px] h-[11px] rounded-[2px] bg-muted" />
450
+ <div class="w-[11px] h-[11px] rounded-[2px] bg-primary/20" />
451
+ <div class="w-[11px] h-[11px] rounded-[2px] bg-primary/40" />
452
+ <div class="w-[11px] h-[11px] rounded-[2px] bg-primary/60" />
453
+ <div class="w-[11px] h-[11px] rounded-[2px] bg-primary" />
420
454
<span>More</span>
421
455
</div>
422
456
</div>
423
457
</div>
424
458
</div>
425
459
426
- <!-- Activity -->
427
- {recentActivity.length > 0 && (
428
- <div>
429
- <div class="text-sm font-semibold text-foreground mb-3">Contribution activity</div>
430
- <div class="relative border-l-2 border-border ml-3 space-y-6 pb-4">
431
- {recentActivity.map((activity) => (
432
- <div class="relative">
433
- <div class="absolute -left-[23px] top-0.5 h-6 w-6 bg-background border border-border rounded-full flex items-center justify-center">
434
- <div class="h-2 w-2 rounded-full bg-green-500" />
435
- </div>
436
- <div class="pl-6">
437
- <p class="text-sm text-foreground">
438
- {activity.action || "Performed an action"}
439
- {activity.targetType && ` on ${activity.targetType}`}
440
- </p>
441
- <p class="text-xs text-muted-foreground mt-0.5">
442
- {timeAgo(activity.createdAt)}
443
- </p>
460
+ <!-- Contribution Activity -->
461
+ <div class="border border-border bg-card rounded-lg p-5">
462
+ <div class="flex items-center gap-2 mb-5">
463
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="text-muted-foreground">
464
+ <path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
465
+ </svg>
466
+ <h3 class="text-base font-semibold text-foreground">Contribution activity</h3>
467
+ </div>
468
+
469
+ {recentActivity.length > 0 ? (
470
+ <div class="space-y-0">
471
+ {recentActivity.map((activity) => {
472
+ const activityDate = new Date(activity.createdAt);
473
+ const dateStr = activityDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
474
+ const timeStr = activityDate.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
475
+
476
+ // Get icon, color, and action text
477
+ const getActivityDisplay = (type: string, action: string) => {
478
+ if (type === 'fork' || action === 'forked') return { iconBg: 'bg-blue-500/15', iconColor: 'text-blue-400', actionColor: 'text-blue-400', action: 'Forked', target: 'a repository' };
479
+ if (type === 'star' || action === 'starred') return { iconBg: 'bg-yellow-500/15', iconColor: 'text-yellow-400', actionColor: 'text-yellow-400', action: 'Starred', target: 'a repository' };
480
+ if (type === 'create_repo' || action === 'created') return { iconBg: 'bg-green-500/15', iconColor: 'text-green-400', actionColor: 'text-green-400', action: 'Created', target: 'a repository' };
481
+ if (type === 'open_issue' || (action === 'opened' && type === 'issue')) return { iconBg: 'bg-green-500/15', iconColor: 'text-green-400', actionColor: 'text-green-400', action: 'Opened', target: 'an issue' };
482
+ if (type === 'open_pr' || type === 'pull_request') return { iconBg: 'bg-green-500/15', iconColor: 'text-green-400', actionColor: 'text-green-400', action: 'Opened', target: 'a pull request' };
483
+ if (type === 'close_issue') return { iconBg: 'bg-red-500/15', iconColor: 'text-red-400', actionColor: 'text-red-400', action: 'Closed', target: 'an issue' };
484
+ if (type === 'close_pr') return { iconBg: 'bg-red-500/15', iconColor: 'text-red-400', actionColor: 'text-red-400', action: 'Closed', target: 'a pull request' };
485
+ if (type === 'merge_pr') return { iconBg: 'bg-purple-500/15', iconColor: 'text-purple-400', actionColor: 'text-purple-400', action: 'Merged', target: 'a pull request' };
486
+ if (type === 'push') return { iconBg: 'bg-green-500/15', iconColor: 'text-green-400', actionColor: 'text-green-400', action: 'Pushed', target: 'commits' };
487
+ if (type === 'comment') return { iconBg: 'bg-blue-500/15', iconColor: 'text-blue-400', actionColor: 'text-blue-400', action: 'Commented', target: 'on an issue' };
488
+ if (type === 'follow') return { iconBg: 'bg-purple-500/15', iconColor: 'text-purple-400', actionColor: 'text-purple-400', action: 'Followed', target: 'a user' };
489
+ return { iconBg: 'bg-muted', iconColor: 'text-muted-foreground', actionColor: 'text-foreground', action: action || 'Performed', target: 'an action' };
490
+ };
491
+
492
+ const display = getActivityDisplay(activity.type, activity.action);
493
+
494
+ return (
495
+ <div class="flex items-center gap-4 py-3 border-b border-border/50 last:border-0 group">
496
+ {/* Icon Badge */}
497
+ <div class:list={[
498
+ "w-9 h-9 rounded-lg flex items-center justify-center flex-shrink-0",
499
+ display.iconBg
500
+ ]}>
501
+ {activity.type === 'fork' && (
502
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><circle cx="12" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M12 6v9"/><path d="M24 18c0-3-2.5-6-6-6"/></svg>
503
+ )}
504
+ {activity.type === 'star' && (
505
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
506
+ )}
507
+ {activity.type === 'create_repo' && (
508
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
509
+ )}
510
+ {(activity.type === 'open_issue' || activity.type === 'close_issue') && (
511
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
512
+ )}
513
+ {(activity.type === 'open_pr' || activity.type === 'pull_request' || activity.type === 'close_pr' || activity.type === 'merge_pr') && (
514
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/><path d="M11 18H8a2 2 0 0 1-2-2V9"/></svg>
515
+ )}
516
+ {activity.type === 'push' && (
517
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
518
+ )}
519
+ {activity.type === 'comment' && (
520
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
521
+ )}
522
+ {activity.type === 'follow' && (
523
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
524
+ )}
525
+ {!['fork','star','create_repo','open_issue','close_issue','open_pr','pull_request','close_pr','merge_pr','push','comment','follow'].includes(activity.type) && (
526
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:list={display.iconColor}><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
527
+ )}
528
+ </div>
529
+
530
+ {/* Activity Text */}
531
+ <div class="flex-1 min-w-0">
532
+ <p class="text-sm text-foreground">
533
+ <span class:list={["font-medium", display.actionColor]}>{display.action}</span>
534
+ {" "}{display.target}
535
+ </p>
536
+ </div>
537
+
538
+ {/* Date */}
539
+ <span class="text-xs text-muted-foreground whitespace-nowrap flex-shrink-0">
540
+ {dateStr} at {timeStr}
541
+ </span>
444
542
</div>
445
- </div>
446
- ))}
543
+ );
544
+ })}
447
545
</div>
448
- </div>
449
- )}
546
+ ) : (
547
+ <div class="py-8 text-center">
548
+ <p class="text-sm text-muted-foreground">No recent activity</p>
549
+ </div>
550
+ )}
450
551
451
- {recentActivity.length === 0 && (
452
- <div class="text-center py-8 text-sm text-muted-foreground">
453
- No recent activity
454
- </div>
455
- )}
552
+ {recentActivity.length > 0 && (
553
+ <div class="mt-4 text-center">
554
+ <a href={`/${user.username}?tab=activity`} class="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors">
555
+ View all activity
556
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
557
+ </a>
558
+ </div>
559
+ )}
560
+ </div>
456
561
</div>
457
562
)}
458
563
459
564