Skip to content

Commit 94b149d

Browse files
Bhuvanesh SBhuvanesh S
authored andcommitted
feat: add github analytics command center dashboard and widgets
Signed-off-by: Bhuvanesh S <YOUR_GITHUB_EMAIL>
1 parent 42ee1ac commit 94b149d

3 files changed

Lines changed: 493 additions & 0 deletions

File tree

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte';
3+
4+
// Generate some dummy data for the heatmap
5+
// 52 weeks * 7 days
6+
const weeks = 52;
7+
const daysPerWeek = 7;
8+
9+
let heatmapData = $state<{ intensity: number, date: Date }[][]>([]);
10+
11+
onMount(() => {
12+
const today = new Date();
13+
for (let w = 0; w < weeks; w++) {
14+
let weekData = [];
15+
for (let d = 0; d < daysPerWeek; d++) {
16+
const date = new Date(today);
17+
date.setDate(date.getDate() - ((weeks - 1 - w) * 7 + (6 - d)));
18+
// Random intensity between 0 and 4, weighted towards 0
19+
const random = Math.random();
20+
let intensity = 0;
21+
if (random > 0.85) intensity = 4;
22+
else if (random > 0.7) intensity = 3;
23+
else if (random > 0.5) intensity = 2;
24+
else if (random > 0.3) intensity = 1;
25+
26+
weekData.push({ intensity, date });
27+
}
28+
heatmapData.push(weekData);
29+
}
30+
heatmapData = heatmapData; // trigger reactivity
31+
});
32+
33+
function getIntensityColor(intensity: number): string {
34+
switch(intensity) {
35+
case 1: return 'var(--color-level-1)';
36+
case 2: return 'var(--color-level-2)';
37+
case 3: return 'var(--color-level-3)';
38+
case 4: return 'var(--color-level-4)';
39+
default: return 'var(--color-level-0)';
40+
}
41+
}
42+
</script>
43+
44+
<div class="heatmap-container glass">
45+
<div class="heatmap-header">
46+
<h3>Realtime Contributor Activity</h3>
47+
<span class="pulse-indicator">
48+
<span class="pulse-dot"></span> Live
49+
</span>
50+
</div>
51+
52+
<div class="heatmap-scroll-wrapper">
53+
<div class="heatmap-grid">
54+
{#each heatmapData as week, wIndex}
55+
<div class="week">
56+
{#each week as day, dIndex}
57+
<div
58+
class="day"
59+
style="background-color: {getIntensityColor(day.intensity)};"
60+
title="{day.date.toDateString()}: {day.intensity * 3} contributions"
61+
></div>
62+
{/each}
63+
</div>
64+
{/each}
65+
</div>
66+
</div>
67+
68+
<div class="heatmap-legend">
69+
<span class="legend-text">Less</span>
70+
<div class="day" style="background-color: var(--color-level-0);"></div>
71+
<div class="day" style="background-color: var(--color-level-1);"></div>
72+
<div class="day" style="background-color: var(--color-level-2);"></div>
73+
<div class="day" style="background-color: var(--color-level-3);"></div>
74+
<div class="day" style="background-color: var(--color-level-4);"></div>
75+
<span class="legend-text">More</span>
76+
</div>
77+
</div>
78+
79+
<style>
80+
.heatmap-container {
81+
--color-level-0: rgba(100, 116, 139, 0.1);
82+
--color-level-1: rgba(99, 102, 241, 0.4);
83+
--color-level-2: rgba(99, 102, 241, 0.6);
84+
--color-level-3: rgba(99, 102, 241, 0.8);
85+
--color-level-4: var(--primary);
86+
87+
padding: 1.5rem;
88+
border-radius: var(--radius-lg);
89+
display: flex;
90+
flex-direction: column;
91+
gap: 1.5rem;
92+
}
93+
94+
.heatmap-header {
95+
display: flex;
96+
justify-content: space-between;
97+
align-items: center;
98+
}
99+
100+
.heatmap-header h3 {
101+
font-size: 1.125rem;
102+
color: var(--text-primary);
103+
}
104+
105+
.pulse-indicator {
106+
display: flex;
107+
align-items: center;
108+
gap: 0.5rem;
109+
font-size: 0.875rem;
110+
color: var(--text-muted);
111+
font-weight: 500;
112+
}
113+
114+
.pulse-dot {
115+
width: 8px;
116+
height: 8px;
117+
background-color: #22c55e;
118+
border-radius: 50%;
119+
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
120+
animation: pulse 2s infinite;
121+
}
122+
123+
@keyframes pulse {
124+
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); }
125+
70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); }
126+
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
127+
}
128+
129+
.heatmap-scroll-wrapper {
130+
overflow-x: auto;
131+
padding-bottom: 0.5rem;
132+
}
133+
134+
/* Custom scrollbar for heatmap */
135+
.heatmap-scroll-wrapper::-webkit-scrollbar {
136+
height: 6px;
137+
}
138+
.heatmap-scroll-wrapper::-webkit-scrollbar-track {
139+
background: var(--bg-secondary);
140+
border-radius: 999px;
141+
}
142+
.heatmap-scroll-wrapper::-webkit-scrollbar-thumb {
143+
background: rgba(99, 102, 241, 0.3);
144+
border-radius: 999px;
145+
}
146+
147+
.heatmap-grid {
148+
display: flex;
149+
gap: 4px;
150+
min-width: max-content;
151+
}
152+
153+
.week {
154+
display: flex;
155+
flex-direction: column;
156+
gap: 4px;
157+
}
158+
159+
.day {
160+
width: 14px;
161+
height: 14px;
162+
border-radius: 3px;
163+
border: 1px solid rgba(255, 255, 255, 0.05);
164+
transition: transform 0.2s ease, box-shadow 0.2s ease;
165+
cursor: pointer;
166+
}
167+
168+
.day:hover {
169+
transform: scale(1.2);
170+
box-shadow: 0 0 8px var(--primary-glow);
171+
z-index: 10;
172+
}
173+
174+
.heatmap-legend {
175+
display: flex;
176+
align-items: center;
177+
gap: 0.5rem;
178+
align-self: flex-end;
179+
font-size: 0.75rem;
180+
color: var(--text-muted);
181+
}
182+
183+
.legend-text {
184+
margin: 0 0.25rem;
185+
}
186+
</style>
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte';
3+
let {
4+
title,
5+
value,
6+
trend,
7+
isPositive = true,
8+
icon = '📊'
9+
} = $props<{
10+
title: string;
11+
value: string;
12+
trend: string;
13+
isPositive?: boolean;
14+
icon?: string;
15+
}>();
16+
17+
let widgetRef: HTMLDivElement;
18+
</script>
19+
20+
<div class="analytics-widget glass" bind:this={widgetRef}>
21+
<div class="widget-header">
22+
<div class="icon-wrapper">
23+
<span class="icon">{icon}</span>
24+
</div>
25+
<span class="trend {isPositive ? 'positive' : 'negative'}">
26+
{isPositive ? '' : ''} {trend}
27+
</span>
28+
</div>
29+
30+
<div class="widget-content">
31+
<h3 class="title">{title}</h3>
32+
<div class="value gradient-text">{value}</div>
33+
</div>
34+
</div>
35+
36+
<style>
37+
.analytics-widget {
38+
padding: 1.5rem;
39+
border-radius: var(--radius-lg);
40+
display: flex;
41+
flex-direction: column;
42+
gap: 1rem;
43+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease;
44+
cursor: default;
45+
position: relative;
46+
overflow: hidden;
47+
}
48+
49+
.analytics-widget::before {
50+
content: '';
51+
position: absolute;
52+
top: 0;
53+
left: 0;
54+
right: 0;
55+
bottom: 0;
56+
background: radial-gradient(circle at top right, var(--primary-glow), transparent 70%);
57+
opacity: 0;
58+
transition: opacity 0.3s ease;
59+
pointer-events: none;
60+
}
61+
62+
.analytics-widget:hover {
63+
transform: translateY(-4px);
64+
box-shadow: var(--shadow-lg);
65+
}
66+
67+
.analytics-widget:hover::before {
68+
opacity: 1;
69+
}
70+
71+
.widget-header {
72+
display: flex;
73+
justify-content: space-between;
74+
align-items: center;
75+
}
76+
77+
.icon-wrapper {
78+
width: 42px;
79+
height: 42px;
80+
border-radius: 50%;
81+
background: var(--bg-secondary);
82+
display: flex;
83+
align-items: center;
84+
justify-content: center;
85+
border: 1px solid var(--border-glass);
86+
}
87+
88+
.icon {
89+
font-size: 1.25rem;
90+
}
91+
92+
.trend {
93+
font-size: 0.875rem;
94+
font-weight: 600;
95+
padding: 0.25rem 0.75rem;
96+
border-radius: 999px;
97+
}
98+
99+
.trend.positive {
100+
background: rgba(34, 197, 94, 0.15);
101+
color: #22c55e;
102+
}
103+
104+
.trend.negative {
105+
background: rgba(239, 68, 68, 0.15);
106+
color: #ef4444;
107+
}
108+
109+
.widget-content {
110+
display: flex;
111+
flex-direction: column;
112+
gap: 0.25rem;
113+
}
114+
115+
.title {
116+
font-size: 0.875rem;
117+
color: var(--text-muted);
118+
text-transform: uppercase;
119+
letter-spacing: 0.05em;
120+
font-family: 'Inter', sans-serif;
121+
}
122+
123+
.value {
124+
font-size: 2.25rem;
125+
font-weight: 800;
126+
font-family: 'Outfit', sans-serif;
127+
}
128+
</style>

0 commit comments

Comments
 (0)