Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@
"vitest": "^4.0.6"
},
"dependencies": {
"@gelatocloud/gasless": "^0.0.12",
"@apollo/client": "^3.11.8",
"@drips-network/sdk": "0.1.0-alpha.15",
"@efstajas/svelte-stored-writable": "^1.0.0",
"@efstajas/versioned-parser": "^0.1.4",
"@ethereum-attestation-service/eas-sdk": "^2.7.0",
"@gelatocloud/gasless": "^0.0.12",
"@grafana/faro-web-sdk": "^1.18.1",
"@grafana/faro-web-tracing": "^1.18.1",
"@intercom/messenger-js-sdk": "^0.0.18",
Expand All @@ -114,6 +114,7 @@
"@web3-onboard/injected-wallets": "^2.10.17",
"@web3-onboard/walletconnect": "^2.5.5",
"bezier-easing": "^2.1.0",
"chart.js": "^4.5.1",
"csv-simple-parser": "^1.0.3",
"cupertino-pane": "^1.5.4",
"ethereum-blockies-base64": "^1.0.2",
Expand Down
15 changes: 15 additions & 0 deletions src/lib/components/icons/ChartBar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import IconWrapper from './IconWrapper.svelte';

interface Props {
style?: string | undefined;
}

let { style = undefined }: Props = $props();
</script>

<IconWrapper {style}>
<rect x="4" y="14" width="4" height="6" rx="1" />
<rect x="10" y="10" width="4" height="10" rx="1" />
<rect x="16" y="5" width="4" height="15" rx="1" />
</IconWrapper>
17 changes: 17 additions & 0 deletions src/lib/utils/wave/stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { authenticatedCall } from './call';
import { contributorStatsResponseSchema, contributorAiSummaryResponseSchema } from './types/stats';
import parseRes from './utils/parse-res';

export async function getContributorStats(f = fetch) {
return parseRes(
contributorStatsResponseSchema,
await authenticatedCall(f, '/api/stats/contributor'),
);
}

export async function getContributorAiSummary(f = fetch) {
return parseRes(
contributorAiSummaryResponseSchema,
await authenticatedCall(f, '/api/stats/contributor/ai-summary'),
);
}
86 changes: 86 additions & 0 deletions src/lib/utils/wave/types/stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import z from 'zod';

// ====== Points Stats ======

export const contributorWavePointsSchema = z.object({
waveId: z.string(),
waveNumber: z.number().int(),
waveProgramId: z.string(),
waveProgramName: z.string(),
waveProgramSlug: z.string(),
points: z.number().int(),
});

export const contributorPointsStatsSchema = z.object({
totalAllTime: z.number().int(),
byWave: z.array(contributorWavePointsSchema),
});

// ====== Issue Stats ======

export const contributorIssueStatsSchema = z.object({
totalResolved: z.number().int(),
byComplexity: z.object({
small: z.number().int(),
medium: z.number().int(),
large: z.number().int(),
unset: z.number().int(),
}),
});

// ====== Leaderboard Stats ======

export const contributorLeaderboardWaveEntrySchema = z.object({
waveId: z.string(),
waveNumber: z.number().int(),
rank: z.number().int(),
totalParticipants: z.number().int(),
points: z.number().int(),
});

export const contributorLeaderboardProgramSchema = z.object({
waveProgramId: z.string(),
waveProgramName: z.string(),
waveProgramSlug: z.string(),
waves: z.array(contributorLeaderboardWaveEntrySchema),
});

export const contributorLeaderboardStatsSchema = z.object({
byProgram: z.array(contributorLeaderboardProgramSchema),
});

// ====== Review Stats ======

export const contributorReviewStatsSchema = z.object({
totalReceived: z.number().int(),
experienceDistribution: z.object({
exceededExpectations: z.number().int(),
alright: z.number().int(),
belowExpectations: z.number().int(),
}),
averageRatings: z.object({
communicationQuality: z.number().nullable(),
codeQuality: z.number().nullable(),
timeliness: z.number().nullable(),
problemSolving: z.number().nullable(),
}),
});

// ====== Combined Response ======

export const contributorStatsResponseSchema = z.object({
points: contributorPointsStatsSchema,
issues: contributorIssueStatsSchema,
leaderboard: contributorLeaderboardStatsSchema,
reviews: contributorReviewStatsSchema.nullable(),
});
export type ContributorStatsResponse = z.infer<typeof contributorStatsResponseSchema>;

// ====== AI Summary Response ======

export const contributorAiSummaryResponseSchema = z.object({
summary: z.string().nullable(),
generatedAt: z.string().nullable(),
reviewCount: z.number().int().nullable(),
});
export type ContributorAiSummaryResponse = z.infer<typeof contributorAiSummaryResponseSchema>;
7 changes: 7 additions & 0 deletions src/routes/(pages)/wave/(base-layout)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import Wave from '$lib/components/icons/Wave.svelte';
import Wallet from '$lib/components/icons/Wallet.svelte';
import Shield from '$lib/components/icons/Shield.svelte';
import ChartBar from '$lib/components/icons/ChartBar.svelte';

let {
data,
Expand Down Expand Up @@ -111,6 +112,12 @@
href: '/wave/rewards',
icon: Wallet,
},
{
type: 'target' as const,
name: 'My Stats',
href: '/wave/stats/me',
icon: ChartBar,
},
{
type: 'target' as const,
name: 'Points history',
Expand Down
Loading