import { createSignal, onMount, For, Show } from 'solid-js'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { IconCalendar, IconTrendingUp, IconBook, IconFolder, IconExternalLink, IconGitBranch, IconGitMerge, IconGitPullRequest, IconGitCommit } from '@tabler/icons-solidjs'; import { isDemoMode } from '@/lib/demo-mode'; interface ActivityData { date: string; count: number; level: number; // 0-5 intensity level } interface ActivityEvent { type: 'push' | 'pull_request' | 'merge' | 'issue' | 'bookmark' | 'project' | 'learning' | 'note' | 'commit'; title: string; date: string; link?: string; repo?: string; action?: string; } interface GitHubActivityProps { title?: string; showStats?: boolean; showContributionGraph?: boolean; showRecentActivity?: boolean; compact?: boolean; period?: 'year' | 'month' | 'week'; customEvents?: ActivityEvent[]; hideHeader?: boolean; fullWidth?: boolean; externalData?: { contributions: Array<{ date: string; count: number; level: number; }>; total_count: number; }; } export const GitHubActivity = (props: GitHubActivityProps) => { const [activities, setActivities] = createSignal([]); const [recentEvents, setRecentEvents] = createSignal([]); const [selectedPeriod, setSelectedPeriod] = createSignal<'year' | 'month' | 'week'>(props.period || 'year'); const [stats, setStats] = createSignal({ totalContributions: 0, currentStreak: 0, longestStreak: 0 }); const setEmptyData = () => { setActivities([]); setRecentEvents(props.customEvents || []); setStats({ totalContributions: 0, currentStreak: 0, longestStreak: 0 }); }; const setDemoData = () => { // Generate mock contribution data for the last year const mockActivities: ActivityData[] = []; const today = new Date(); for (let i = 364; i >= 0; i--) { const date = new Date(today); date.setDate(date.getDate() - i); // Random activity level (0-5), with higher probability of 0-2 const random = Math.random(); let level = 0; if (random > 0.7) level = 1; if (random > 0.85) level = 2; if (random > 0.93) level = 3; if (random > 0.97) level = 4; if (random > 0.99) level = 5; mockActivities.push({ date: date.toISOString().split('T')[0], count: level, level: level }); } // Calculate stats const totalContributions = mockActivities.reduce((sum, day) => sum + day.count, 0); const currentStreak = Math.floor(Math.random() * 15) + 5; // 5-20 days const longestStreak = Math.floor(Math.random() * 30) + 20; // 20-50 days // Mock recent events const mockEvents: ActivityEvent[] = [ { type: 'push', title: 'Pushed 3 commits to trackeep/frontend', date: '2 hours ago', repo: 'trackeep', action: 'pushed' }, { type: 'pull_request', title: 'Opened PR: Add dark mode support', date: '1 day ago', repo: 'trackeep', action: 'opened PR' }, { type: 'merge', title: 'Merged PR: Fix responsive design issues', date: '2 days ago', repo: 'trackeep', action: 'merged' }, { type: 'commit', title: 'Commit: Update API documentation', date: '3 days ago', repo: 'trackeep', action: 'committed' }, { type: 'push', title: 'Pushed 5 commits to trackeep/backend', date: '1 week ago', repo: 'trackeep', action: 'pushed' } ]; setActivities(mockActivities); setRecentEvents(mockEvents); setStats({ totalContributions, currentStreak, longestStreak }); }; onMount(() => { // Listen for external GitHub activity data const handleGitHubActivityData = (event: CustomEvent) => { const data = event.detail as { contributions: Array<{ date: string; count: number; level: number; }>; total_count: number; }; if (data.contributions && data.contributions.length > 0) { const activityData = data.contributions.map(c => ({ date: c.date, count: c.count, level: c.level })); setActivities(activityData); setStats(prev => ({ ...prev, totalContributions: data.total_count })); } }; window.addEventListener('githubActivityData', handleGitHubActivityData as EventListener); if (props.externalData) { // Use provided external data const activityData = props.externalData.contributions.map(c => ({ date: c.date, count: c.count, level: c.level })); setActivities(activityData); setStats(prev => ({ ...prev, totalContributions: props.externalData!.total_count })); } else if (isDemoMode()) { setDemoData(); } else { setEmptyData(); } return () => { window.removeEventListener('githubActivityData', handleGitHubActivityData as EventListener); }; }); const getMonthLabels = () => { const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const today = new Date(); const labels = []; for (let i = 11; i >= 0; i--) { const date = new Date(today); date.setMonth(date.getMonth() - i); labels.push(months[date.getMonth()]); } return labels; }; const getActivityColor = (level: number) => { // Use project-themed colors instead of Christmas tree colors // Based on the primary theme color with varying intensities const colors = [ 'hsl(var(--muted) / 0.3)', // Level 0 - no activity (very light muted) 'hsl(var(--primary) / 0.2)', // Level 1 - very light primary 'hsl(var(--primary) / 0.4)', // Level 2 - light primary 'hsl(var(--primary) / 0.6)', // Level 3 - medium primary 'hsl(var(--primary) / 0.8)', // Level 4 - strong primary 'hsl(var(--primary))' // Level 5 - full primary ]; return colors[level] || colors[0]; }; const formatContributionCount = (count: number) => { if (count >= 1000) { return `${(count / 1000).toFixed(1)}k`; } return count.toString(); }; const getEventIcon = (type: ActivityEvent['type']) => { switch (type) { case 'push': case 'commit': return ; case 'pull_request': return ; case 'merge': return ; case 'issue': return ; case 'bookmark': return ; case 'project': return ; case 'learning': return ; case 'note': return ; default: return ; } }; return (
{/* Header (can be hidden by parent) */} {!props.hideHeader && (

{props.title || 'Activity Overview'}

Track your contributions and activity over time

{(['year', 'month', 'week'] as const).map((period) => ( ))}
)} {/* Stats Overview */}

{formatContributionCount(stats().totalContributions)}

Total contributions

{stats().currentStreak}

Current streak

{stats().longestStreak}

Longest streak

{/* Contribution Graph */}

{formatContributionCount(stats().totalContributions)} contributions in the last year

0} fallback={

No GitHub contribution data yet.

} > {/* Month labels - Show all months with responsive spacing */}
{getMonthLabels().map((month) => ( {month} ))}
{/* Contribution grid - Responsive and prevents overflow */}
{/* Day labels */}
{['Mon', 'Wed', 'Fri'].map((day) => (
{day}
))}
{/* Weekly columns - Responsive with proper overflow handling */}
{Array.from({ length: 53 }, (_, weekIndex) => (
{Array.from({ length: 7 }, (_, dayIndex) => { const activityIndex = weekIndex * 7 + dayIndex; const activity = activities()[activityIndex]; if (!activity) { return (
); } return (
); })}
))}
{/* Legend */}
Less
{[0, 1, 2, 3, 4].map((level) => (
))}
More
{/* Recent Activity */}

Recent Activity

Active
0} fallback={

No GitHub events yet.

} >
{(event) => (
{getEventIcon(event.type)}

{event.title}

{event.date} {event.repo && ( <> {event.repo} )} {event.action && ( <> {event.action} )}
{event.link && ( )}
)}
); };