mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-11 05:13:02 -05:00
feat: create leaderboard
This commit is contained in:
93
docs/src/components/leaderboard/LeaderboardAnswer.svelte
Normal file
93
docs/src/components/leaderboard/LeaderboardAnswer.svelte
Normal file
@@ -0,0 +1,93 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import UserBox from './UserBox.svelte';
|
||||
|
||||
let users = [];
|
||||
let loading = true;
|
||||
let error = null;
|
||||
|
||||
async function fetchGitHubUsers() {
|
||||
try {
|
||||
const prCounts = {};
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:%22answer%22&per_page=200&page=${page}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('API rate limit exceeded. Please try again in a few minutes.');
|
||||
}
|
||||
const { total_count, items } = await response.json();
|
||||
|
||||
if (!items || items.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
items.forEach(pr => {
|
||||
const userLogin = pr.user.login;
|
||||
if (prCounts[userLogin]) {
|
||||
prCounts[userLogin].count++;
|
||||
prCounts[userLogin].challengeNumber.push(pr.labels.filter(l => !isNaN(Number(l.name))).map(l => Number(l.name))?.[0]);
|
||||
} else {
|
||||
prCounts[userLogin] = {
|
||||
avatar: pr.user.avatar_url,
|
||||
count: 1,
|
||||
challengeNumber: [pr.labels.filter(l => !isNaN(Number(l.name))).map(l => Number(l.name))?.[0]]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if(total_count < page * 100) {
|
||||
break;
|
||||
}
|
||||
|
||||
page++;
|
||||
}
|
||||
|
||||
users = Object.entries(prCounts).map(([login, pr]) => ({
|
||||
login,
|
||||
avatar: pr.avatar,
|
||||
count: pr.count,
|
||||
challengeNumber: pr.challengeNumber.sort((a, b) => a - b)
|
||||
})).filter((r) => r.login !== 'allcontributors[bot]').sort((a, b) => b.count - a.count);
|
||||
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchGitHubUsers();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<p>Loading...</p>
|
||||
{:else if error}
|
||||
<p>Error: {error}</p>
|
||||
{:else}
|
||||
<div class="box not-content">
|
||||
{#each users as { avatar, count, login,challengeNumber }, index}
|
||||
<UserBox {avatar} {login} {index}>
|
||||
{count} Answers
|
||||
<div slot="addon" class="challenge-number">{challengeNumber.join(', ')}</div>
|
||||
</UserBox>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.box {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.challenge-number {
|
||||
font-size: 0.7rem;
|
||||
color: var(--sl-color-gray-3);
|
||||
}
|
||||
</style>
|
||||
72
docs/src/components/leaderboard/LeaderboardChallenge.svelte
Normal file
72
docs/src/components/leaderboard/LeaderboardChallenge.svelte
Normal file
@@ -0,0 +1,72 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import UserBox from './UserBox.svelte';
|
||||
|
||||
|
||||
let users = [];
|
||||
let loading = true;
|
||||
let error = null;
|
||||
|
||||
const createUser = (items) => {
|
||||
const prCounts = {};
|
||||
items.forEach((pr) => {
|
||||
const userLogin = pr.user.login;
|
||||
if (prCounts[userLogin]) {
|
||||
prCounts[userLogin].count++;
|
||||
} else {
|
||||
prCounts[userLogin] = {
|
||||
avatar: pr.user.avatar_url,
|
||||
count: 1
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return Object.entries(prCounts).map(([login, pr]) => ({
|
||||
login,
|
||||
avatar: pr.avatar,
|
||||
count: pr.count
|
||||
})).filter((r) => r.login !== 'allcontributors[bot]').sort((a, b) => b.count - a.count);
|
||||
};
|
||||
|
||||
async function fetchGitHubUsers() {
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:%22challenge-creation%22`);
|
||||
if (!response.ok) {
|
||||
throw new Error('API rate limit exceeded. Please try again in a few minutes.');
|
||||
}
|
||||
const { items } = await response.json();
|
||||
users = createUser(items);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchGitHubUsers();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<p>Loading...</p>
|
||||
{:else if error}
|
||||
<p>Error: {error}</p>
|
||||
{:else}
|
||||
<div class="box not-content">
|
||||
{#each users as { avatar, count, login, challengeNumber }, index}
|
||||
<UserBox {avatar} {login} {index}>
|
||||
{count} Challenges Created
|
||||
</UserBox>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.box {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
88
docs/src/components/leaderboard/LeaderboardCommit.svelte
Normal file
88
docs/src/components/leaderboard/LeaderboardCommit.svelte
Normal file
@@ -0,0 +1,88 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import UserBox from './UserBox.svelte';
|
||||
|
||||
let users = [];
|
||||
let loading = true;
|
||||
let error = null;
|
||||
|
||||
async function fetchGitHubUsers() {
|
||||
try {
|
||||
const prCounts = {};
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+no:label&per_page=100&page=${page}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('API rate limit exceeded. Please try again in a few minutes.');
|
||||
}
|
||||
const { total_count, items } = await response.json();
|
||||
|
||||
if (!items || items.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
items.forEach(pr => {
|
||||
const userLogin = pr.user.login;
|
||||
if (prCounts[userLogin]) {
|
||||
prCounts[userLogin].count++;
|
||||
prCounts[userLogin].challengeNumber.push(pr.labels.filter(l => !isNaN(Number(l.name))).map(l => l.name).join(', '));
|
||||
} else {
|
||||
prCounts[userLogin] = {
|
||||
avatar: pr.user.avatar_url,
|
||||
count: 1,
|
||||
challengeNumber: [pr.labels.filter(l => !isNaN(Number(l.name))).map(l => l.name).join(', ')]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if(total_count < page * 100) {
|
||||
break;
|
||||
}
|
||||
|
||||
page++;
|
||||
|
||||
}
|
||||
|
||||
users = Object.entries(prCounts).map(([login, pr]) => ({
|
||||
login,
|
||||
avatar: pr.avatar,
|
||||
count: pr.count,
|
||||
challengeNumber: pr.challengeNumber
|
||||
})).filter((r) => r.login !== 'allcontributors[bot]').sort((a, b) => b.count - a.count);
|
||||
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchGitHubUsers();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<p>Loading...</p>
|
||||
{:else if error}
|
||||
<p>Error: {error}</p>
|
||||
{:else}
|
||||
<div class="box not-content">
|
||||
{#each users as { avatar, count, login }, index}
|
||||
<UserBox {avatar} {login} {index}>
|
||||
{count} PRs merged
|
||||
</UserBox>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.box {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
77
docs/src/components/leaderboard/UserBox.svelte
Normal file
77
docs/src/components/leaderboard/UserBox.svelte
Normal file
@@ -0,0 +1,77 @@
|
||||
<script>
|
||||
export let avatar;
|
||||
export let login;
|
||||
export let index;
|
||||
</script>
|
||||
|
||||
<div class="user-box">
|
||||
<div class="user-info">
|
||||
<img src={avatar} alt="" width="40" height="40" class="avatar" />
|
||||
<div class="name-box">
|
||||
<div class="user-name">{login}</div>
|
||||
<div class="count"><slot /></div>
|
||||
<slot name="addon" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="position">
|
||||
#{index + 1}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.position {
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
height: 100%;
|
||||
padding: 0.5rem;
|
||||
color: var(--sl-color-gray-6);
|
||||
background-color: var(--sl-color-gray-3);
|
||||
}
|
||||
|
||||
.user-box {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
border: 1px solid var(--sl-color-gray-3);
|
||||
border-radius: 5px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
padding: 1rem 0 1rem 1rem;
|
||||
gap: 1rem;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px
|
||||
}
|
||||
|
||||
.name-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 24px;
|
||||
color: red;
|
||||
line-height: 24px;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: var(--sl-text-xs);
|
||||
color: var(--sl-color-gray-3);
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user